Remove typo from RAPI documentation
[ganeti-local] / doc / rapi.rst
index 2ab5afc..fba7837 100644 (file)
@@ -16,6 +16,7 @@ it runs on TCP port 5080, but this can be changed either in
 which is used by default, can also be disabled by passing command line
 parameters.
 
+.. _rapi-users:
 
 Users and passwords
 -------------------
@@ -24,12 +25,11 @@ Users and passwords
 ``/var/lib/ganeti/rapi/users``) on startup. Changes to the file will be
 read automatically.
 
-Each line consists of two or three fields separated by whitespace. The
-first two fields are for username and password. The third field is
-optional and can be used to specify per-user options. Currently,
-``write`` is the only option supported and enables the user to execute
-operations modifying the cluster. Lines starting with the hash sign
-(``#``) are treated as comments.
+Lines starting with the hash sign (``#``) are treated as comments. Each
+line consists of two or three fields separated by whitespace. The first
+two fields are for username and password. The third field is optional
+and can be used to specify per-user options (separated by comma without
+spaces).
 
 Passwords can either be written in clear text or as a hash. Clear text
 passwords may not start with an opening brace (``{``) or they must be
@@ -37,7 +37,40 @@ prefixed with ``{cleartext}``. To use the hashed form, get the MD5 hash
 of the string ``$username:Ganeti Remote API:$password`` (e.g. ``echo -n
 'jack:Ganeti Remote API:abc123' | openssl md5``) [#pwhash]_ and prefix
 it with ``{ha1}``. Using the scheme prefix for all passwords is
-recommended. Scheme prefixes are not case sensitive.
+recommended. Scheme prefixes are case insensitive.
+
+Options control a user's access permissions. The section
+:ref:`rapi-access-permissions` lists the permissions required for each
+resource. If the ``--require-authentication`` command line option is
+given to the ``ganeti-rapi`` daemon, all requests require
+authentication. Available options:
+
+.. pyassert::
+
+  rapi.RAPI_ACCESS_ALL == set([
+    rapi.RAPI_ACCESS_WRITE,
+    rapi.RAPI_ACCESS_READ,
+    ])
+
+.. pyassert::
+
+  rlib2.R_2_nodes_name_storage.GET_ACCESS == [rapi.RAPI_ACCESS_WRITE]
+
+.. pyassert::
+
+  rlib2.R_2_jobs_id_wait.GET_ACCESS == [rapi.RAPI_ACCESS_WRITE]
+
+:pyeval:`rapi.RAPI_ACCESS_WRITE`
+  Enables the user to execute operations modifying the cluster. Implies
+  :pyeval:`rapi.RAPI_ACCESS_READ` access. Resources blocking other
+  operations for read-only access, such as
+  :ref:`/2/nodes/[node_name]/storage <rapi-res-nodes-node_name-storage+get>`
+  or blocking server-side processes, such as
+  :ref:`/2/jobs/[job_id]/wait <rapi-res-jobs-job_id-wait+get>`, use
+  :pyeval:`rapi.RAPI_ACCESS_WRITE` to control access to their
+  :pyeval:`http.HTTP_GET` method.
+:pyeval:`rapi.RAPI_ACCESS_READ`
+  Allow access to operations querying for information.
 
 Example::
 
@@ -51,10 +84,24 @@ Example::
   # Hashed password for Jessica
   jessica {HA1}7046452df2cbb530877058712cf17bd4 write
 
+  # Monitoring can query for values
+  monitoring {HA1}ec018ffe72b8e75bb4d508ed5b6d079c read
+
+  # A user who can read and write (the former is implied by granting
+  # write access)
+  superuser {HA1}ec018ffe72b8e75bb4d508ed5b6d079c read,write
+
+When using the RAPI, username and password can be sent to the server
+by using the standard HTTP basic access authentication. This means that
+for accessing the protected URL ``https://cluster.example.com/resource``,
+the address ``https://username:password@cluster.example.com/resource`` should
+be used instead.
+Alternatively, the appropriate parameter of your HTTP client
+(such as ``-u`` for ``curl``) can be used.
 
 .. [#pwhash] Using the MD5 hash of username, realm and password is
-   described in :rfc:`2617` ("HTTP Authentication"), sections 3.2.2.2 and
-   3.3. The reason for using it over another algorithm is forward
+   described in :rfc:`2617` ("HTTP Authentication"), sections 3.2.2.2
+   and 3.3. The reason for using it over another algorithm is forward
    compatibility. If ``ganeti-rapi`` were to implement HTTP Digest
    authentication in the future, the same hash could be used.
    In the current version ``ganeti-rapi``'s realm, ``Ganeti Remote
@@ -166,6 +213,74 @@ likely to succeed or at least start executing.
 Force operation to continue even if it will cause the cluster to become
 inconsistent (e.g. because there are not enough master candidates).
 
+Parameter details
+-----------------
+
+Some parameters are not straight forward, so we describe them in details
+here.
+
+.. _rapi-ipolicy:
+
+``ipolicy``
++++++++++++
+
+The instance policy specification is a dict with the following fields:
+
+.. pyassert::
+
+  constants.IPOLICY_ALL_KEYS == set([constants.ISPECS_MINMAX,
+                                     constants.ISPECS_STD,
+                                     constants.IPOLICY_DTS,
+                                     constants.IPOLICY_VCPU_RATIO,
+                                     constants.IPOLICY_SPINDLE_RATIO])
+
+
+.. pyassert::
+
+  (set(constants.ISPECS_PARAMETER_TYPES.keys()) ==
+   set([constants.ISPEC_MEM_SIZE,
+        constants.ISPEC_DISK_SIZE,
+        constants.ISPEC_DISK_COUNT,
+        constants.ISPEC_CPU_COUNT,
+        constants.ISPEC_NIC_COUNT,
+        constants.ISPEC_SPINDLE_USE]))
+
+.. |ispec-min| replace:: :pyeval:`constants.ISPECS_MIN`
+.. |ispec-max| replace:: :pyeval:`constants.ISPECS_MAX`
+.. |ispec-std| replace:: :pyeval:`constants.ISPECS_STD`
+
+
+:pyeval:`constants.ISPECS_MINMAX`
+  A list of dictionaries, each with the following two fields:
+
+  |ispec-min|, |ispec-max|
+    A sub- `dict` with the following fields, which sets the limit of the
+    instances:
+
+    :pyeval:`constants.ISPEC_MEM_SIZE`
+      The size in MiB of the memory used
+    :pyeval:`constants.ISPEC_DISK_SIZE`
+      The size in MiB of the disk used
+    :pyeval:`constants.ISPEC_DISK_COUNT`
+      The numbers of disks used
+    :pyeval:`constants.ISPEC_CPU_COUNT`
+      The numbers of cpus used
+    :pyeval:`constants.ISPEC_NIC_COUNT`
+      The numbers of nics used
+    :pyeval:`constants.ISPEC_SPINDLE_USE`
+      The numbers of virtual disk spindles used by this instance. They
+      are not real in the sense of actual HDD spindles, but useful for
+      accounting the spindle usage on the residing node
+|ispec-std|
+  A sub- `dict` with the same fields as |ispec-min| and |ispec-max| above,
+  which sets the standard values of the instances.
+:pyeval:`constants.IPOLICY_DTS`
+  A `list` of disk templates allowed for instances using this policy
+:pyeval:`constants.IPOLICY_VCPU_RATIO`
+  Maximum ratio of virtual to physical CPUs (`float`)
+:pyeval:`constants.IPOLICY_SPINDLE_RATIO`
+  Maximum ratio of instances to their node's ``spindle_count`` (`float`)
+
 Usage examples
 --------------
 
@@ -180,16 +295,21 @@ Ganeti includes a standalone RAPI client, ``lib/rapi/client.py``.
 Shell
 +++++
 
-.. highlight:: sh
+.. highlight:: shell-example
 
-Using wget::
+Using ``wget``::
 
-   wget -q -O - https://CLUSTERNAME:5080/2/info
+   $ wget -q -O - https://%CLUSTERNAME%:5080/2/info
 
-or curl::
+or ``curl``::
 
-  curl https://CLUSTERNAME:5080/2/info
+  $ curl https://%CLUSTERNAME%:5080/2/info
 
+Note: with ``curl``, the request method (GET, POST, PUT) can be specified
+using the ``-X`` command line option, and the username/password can be
+specified with the ``-u`` option. In case of POST requests with a body, the
+Content-Type can be set to JSON (as per the Protocol_ section) using the
+parameter ``-H "Content-Type: application/json"``.
 
 Python
 ++++++
@@ -239,37 +359,25 @@ Resources
 ``/``
 +++++
 
-The root resource.
-
-It supports the following commands: ``GET``.
-
-``GET``
-~~~~~~~
-
-Shows the list of mapped resources.
-
-Returns: a dictionary with 'name' and 'uri' keys for each of them.
+The root resource. Has no function, but for legacy reasons the ``GET``
+method is supported.
 
 ``/2``
 ++++++
 
-The ``/2`` resource, the root of the version 2 API.
-
-It supports the following commands: ``GET``.
-
-``GET``
-~~~~~~~
+Has no function, but for legacy reasons the ``GET`` method is supported.
 
-Show the list of mapped resources.
-
-Returns: a dictionary with ``name`` and ``uri`` keys for each of them.
+.. _rapi-res-info:
 
 ``/2/info``
 +++++++++++
 
 Cluster information resource.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/info
+
+
+.. _rapi-res-info+get:
 
 ``GET``
 ~~~~~~~
@@ -304,26 +412,43 @@ Example::
         "vcpus": 1,
         "memory": 128
        }
-      }
-    }
+      },
+    …
+  }
+
 
+.. _rapi-res-redistribute-config:
 
 ``/2/redistribute-config``
 ++++++++++++++++++++++++++
 
 Redistribute configuration to all nodes.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/redistribute-config
+
+
+.. _rapi-res-redistribute-config+put:
 
 ``PUT``
 ~~~~~~~
 
 Redistribute configuration to all nodes. The result will be a job id.
 
+Job result:
+
+.. opcode_result:: OP_CLUSTER_REDIST_CONF
+
+
+.. _rapi-res-features:
 
 ``/2/features``
 +++++++++++++++
 
+.. rapi_resource_details:: /2/features
+
+
+.. _rapi-res-features+get:
+
 ``GET``
 ~~~~~~~
 
@@ -338,23 +463,28 @@ features:
                              rlib2._NODE_EVAC_RES1])
 
 :pyeval:`rlib2._INST_CREATE_REQV1`
-  Instance creation request data version 1 supported.
+  Instance creation request data version 1 supported
 :pyeval:`rlib2._INST_REINSTALL_REQV1`
-  Instance reinstall supports body parameters.
+  Instance reinstall supports body parameters
 :pyeval:`rlib2._NODE_MIGRATE_REQV1`
   Whether migrating a node (``/2/nodes/[node_name]/migrate``) supports
-  request body parameters.
+  request body parameters
 :pyeval:`rlib2._NODE_EVAC_RES1`
   Whether evacuating a node (``/2/nodes/[node_name]/evacuate``) returns
   a new-style result (see resource description)
 
 
+.. _rapi-res-modify:
+
 ``/2/modify``
 ++++++++++++++++++++++++++++++++++++++++
 
 Modifies cluster parameters.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/modify
+
+
+.. _rapi-res-modify+put:
 
 ``PUT``
 ~~~~~~~
@@ -365,13 +495,22 @@ Body parameters:
 
 .. opcode_params:: OP_CLUSTER_SET_PARAMS
 
+Job result:
+
+.. opcode_result:: OP_CLUSTER_SET_PARAMS
+
+
+.. _rapi-res-groups:
 
 ``/2/groups``
 +++++++++++++
 
 The groups resource.
 
-It supports the following commands: ``GET``, ``POST``.
+.. rapi_resource_details:: /2/groups
+
+
+.. _rapi-res-groups+get:
 
 ``GET``
 ~~~~~~~
@@ -395,6 +534,8 @@ If the optional bool *bulk* argument is provided and set to a true value
 (i.e ``?bulk=1``), the output contains detailed information about node
 groups as a list.
 
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.G_FIELDS))`.
+
 Example::
 
     [
@@ -405,7 +546,8 @@ Example::
           "node1.example.com",
           "node2.example.com"
         ],
-        "uuid": "0d7d407c-262e-49af-881a-6a430034bf43"
+        "uuid": "0d7d407c-262e-49af-881a-6a430034bf43",
+        …
       },
       {
         "name": "group2",
@@ -413,10 +555,15 @@ Example::
         "node_list": [
           "node3.example.com"
         ],
-        "uuid": "f5a277e7-68f9-44d3-a378-4b25ecb5df5c"
-      }
+        "uuid": "f5a277e7-68f9-44d3-a378-4b25ecb5df5c",
+        …
+      },
+      …
     ]
 
+
+.. _rapi-res-groups+post:
+
 ``POST``
 ~~~~~~~~
 
@@ -434,13 +581,22 @@ Body parameters:
 Earlier versions used a parameter named ``name`` which, while still
 supported, has been renamed to ``group_name``.
 
+Job result:
+
+.. opcode_result:: OP_GROUP_ADD
+
+
+.. _rapi-res-groups-group_name:
 
 ``/2/groups/[group_name]``
 ++++++++++++++++++++++++++
 
 Returns information about a node group.
 
-It supports the following commands: ``GET``, ``DELETE``.
+.. rapi_resource_details:: /2/groups/[group_name]
+
+
+.. _rapi-res-groups-group_name+get:
 
 ``GET``
 ~~~~~~~
@@ -448,6 +604,10 @@ It supports the following commands: ``GET``, ``DELETE``.
 Returns information about a node group, similar to the bulk output from
 the node group list.
 
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.G_FIELDS))`.
+
+.. _rapi-res-groups-group_name+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -455,13 +615,22 @@ Deletes a node group.
 
 It supports the ``dry-run`` argument.
 
+Job result:
+
+.. opcode_result:: OP_GROUP_REMOVE
+
+
+.. _rapi-res-groups-group_name-modify:
 
 ``/2/groups/[group_name]/modify``
 +++++++++++++++++++++++++++++++++
 
 Modifies the parameters of a node group.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/groups/[group_name]/modify
+
+
+.. _rapi-res-groups-group_name-modify+put:
 
 ``PUT``
 ~~~~~~~
@@ -473,13 +642,22 @@ Body parameters:
 .. opcode_params:: OP_GROUP_SET_PARAMS
    :exclude: group_name
 
+Job result:
+
+.. opcode_result:: OP_GROUP_SET_PARAMS
+
+
+.. _rapi-res-groups-group_name-rename:
 
 ``/2/groups/[group_name]/rename``
 +++++++++++++++++++++++++++++++++
 
 Renames a node group.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/groups/[group_name]/rename
+
+
+.. _rapi-res-groups-group_name-rename+put:
 
 ``PUT``
 ~~~~~~~
@@ -491,13 +669,21 @@ Body parameters:
 .. opcode_params:: OP_GROUP_RENAME
    :exclude: group_name
 
+Job result:
+
+.. opcode_result:: OP_GROUP_RENAME
+
+
+.. _rapi-res-groups-group_name-assign-nodes:
 
 ``/2/groups/[group_name]/assign-nodes``
 +++++++++++++++++++++++++++++++++++++++
 
 Assigns nodes to a group.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/groups/[group_name]/assign-nodes
+
+.. _rapi-res-groups-group_name-assign-nodes+put:
 
 ``PUT``
 ~~~~~~~
@@ -509,13 +695,263 @@ Body parameters:
 .. opcode_params:: OP_GROUP_ASSIGN_NODES
    :exclude: group_name, force, dry_run
 
+Job result:
+
+.. opcode_result:: OP_GROUP_ASSIGN_NODES
+
+.. _rapi-res-groups-group_name-tags:
 
 ``/2/groups/[group_name]/tags``
 +++++++++++++++++++++++++++++++
 
 Manages per-nodegroup tags.
 
-Supports the following commands: ``GET``, ``PUT``, ``DELETE``.
+.. rapi_resource_details:: /2/groups/[group_name]/tags
+
+
+.. _rapi-res-groups-group_name-tags+get:
+
+``GET``
+~~~~~~~
+
+Returns a list of tags.
+
+Example::
+
+    ["tag1", "tag2", "tag3"]
+
+.. _rapi-res-groups-group_name-tags+put:
+
+``PUT``
+~~~~~~~
+
+Add a set of tags.
+
+The request as a list of strings should be ``PUT`` to this URI. The
+result will be a job id.
+
+It supports the ``dry-run`` argument.
+
+
+.. _rapi-res-groups-group_name-tags+delete:
+
+``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]
+
+It supports the ``dry-run`` argument.
+
+
+.. _rapi-res-networks:
+
+``/2/networks``
++++++++++++++++
+
+The networks resource.
+
+.. rapi_resource_details:: /2/networks
+
+
+.. _rapi-res-networks+get:
+
+``GET``
+~~~~~~~
+
+Returns a list of all existing networks.
+
+Example::
+
+    [
+      {
+        "name": "network1",
+        "uri": "\/2\/networks\/network1"
+      },
+      {
+        "name": "network2",
+        "uri": "\/2\/networks\/network2"
+      }
+    ]
+
+If the optional bool *bulk* argument is provided and set to a true value
+(i.e ``?bulk=1``), the output contains detailed information about networks
+as a list.
+
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.NET_FIELDS))`.
+
+Example::
+
+    [
+      {
+        'external_reservations': '10.0.0.0, 10.0.0.1, 10.0.0.15',
+        'free_count': 13,
+        'gateway': '10.0.0.1',
+        'gateway6': None,
+        'group_list': ['default(bridged, prv0)'],
+        'inst_list': [],
+        'mac_prefix': None,
+        'map': 'XX.............X',
+        'name': 'nat',
+        'network': '10.0.0.0/28',
+        'network6': None,
+        'reserved_count': 3,
+        'tags': ['nfdhcpd'],
+        …
+      },
+      …
+    ]
+
+
+.. _rapi-res-networks+post:
+
+``POST``
+~~~~~~~~
+
+Creates a network.
+
+If the optional bool *dry-run* argument is provided, the job will not be
+actually executed, only the pre-execution checks will be done.
+
+Returns: a job ID that can be used later for polling.
+
+Body parameters:
+
+.. opcode_params:: OP_NETWORK_ADD
+
+Job result:
+
+.. opcode_result:: OP_NETWORK_ADD
+
+
+.. _rapi-res-networks-network_name:
+
+``/2/networks/[network_name]``
+++++++++++++++++++++++++++++++
+
+Returns information about a network.
+
+.. rapi_resource_details:: /2/networks/[network_name]
+
+
+.. _rapi-res-networks-network_name+get:
+
+``GET``
+~~~~~~~
+
+Returns information about a network, similar to the bulk output from
+the network list.
+
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.NET_FIELDS))`.
+
+
+.. _rapi-res-networks-network_name+delete:
+
+``DELETE``
+~~~~~~~~~~
+
+Deletes a network.
+
+It supports the ``dry-run`` argument.
+
+Job result:
+
+.. opcode_result:: OP_NETWORK_REMOVE
+
+
+.. _rapi-res-networks-network_name-modify:
+
+``/2/networks/[network_name]/modify``
++++++++++++++++++++++++++++++++++++++
+
+Modifies the parameters of a network.
+
+.. rapi_resource_details:: /2/networks/[network_name]/modify
+
+
+.. _rapi-res-networks-network_name-modify+put:
+
+``PUT``
+~~~~~~~
+
+Returns a job ID.
+
+Body parameters:
+
+.. opcode_params:: OP_NETWORK_SET_PARAMS
+
+Job result:
+
+.. opcode_result:: OP_NETWORK_SET_PARAMS
+
+
+.. _rapi-res-networks-network_name-connect:
+
+``/2/networks/[network_name]/connect``
+++++++++++++++++++++++++++++++++++++++
+
+Connects a network to a nodegroup.
+
+.. rapi_resource_details:: /2/networks/[network_name]/connect
+
+
+.. _rapi-res-networks-network_name-connect+put:
+
+``PUT``
+~~~~~~~
+
+Returns a job ID. It supports the ``dry-run`` arguments.
+
+Body parameters:
+
+.. opcode_params:: OP_NETWORK_CONNECT
+
+Job result:
+
+.. opcode_result:: OP_NETWORK_CONNECT
+
+
+.. _rapi-res-networks-network_name-disconnect:
+
+``/2/networks/[network_name]/disconnect``
++++++++++++++++++++++++++++++++++++++++++
+
+Disonnects a network from a nodegroup.
+
+.. rapi_resource_details:: /2/networks/[network_name]/disconnect
+
+
+.. _rapi-res-networks-network_name-disconnect+put:
+
+``PUT``
+~~~~~~~
+
+Returns a job ID. It supports the ``dry-run`` arguments.
+
+Body parameters:
+
+.. opcode_params:: OP_NETWORK_DISCONNECT
+
+Job result:
+
+.. opcode_result:: OP_NETWORK_DISCONNECT
+
+
+.. _rapi-res-networks-network_name-tags:
+
+``/2/networks/[network_name]/tags``
++++++++++++++++++++++++++++++++++++
+
+Manages per-network tags.
+
+.. rapi_resource_details:: /2/networks/[network_name]/tags
+
+
+.. _rapi-res-networks-network_name-tags+get:
 
 ``GET``
 ~~~~~~~
@@ -526,6 +962,9 @@ Example::
 
     ["tag1", "tag2", "tag3"]
 
+
+.. _rapi-res-networks-network_name-tags+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -537,6 +976,8 @@ result will be a job id.
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-networks-network_name-tags+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -550,12 +991,41 @@ to URI like::
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-instances-multi-alloc:
+
+``/2/instances-multi-alloc``
+++++++++++++++++++++++++++++
+
+Tries to allocate multiple instances.
+
+.. rapi_resource_details:: /2/instances-multi-alloc
+
+
+.. _rapi-res-instances-multi-alloc+post:
+
+``POST``
+~~~~~~~~
+
+The parameters:
+
+.. opcode_params:: OP_INSTANCE_MULTI_ALLOC
+
+Job result:
+
+.. opcode_result:: OP_INSTANCE_MULTI_ALLOC
+
+
+.. _rapi-res-instances:
+
 ``/2/instances``
 ++++++++++++++++
 
 The instances resource.
 
-It supports the following commands: ``GET``, ``POST``.
+.. rapi_resource_details:: /2/instances
+
+
+.. _rapi-res-instances+get:
 
 ``GET``
 ~~~~~~~
@@ -579,36 +1049,41 @@ If the optional bool *bulk* argument is provided and set to a true value
 (i.e ``?bulk=1``), the output contains detailed information about
 instances as a list.
 
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.I_FIELDS))`.
+
 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
+        "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,
+        …
       },
-      ...
+      …
     ]
 
 
+.. _rapi-res-instances+post:
+
 ``POST``
 ~~~~~~~~
 
@@ -634,13 +1109,22 @@ Earlier versions used parameters named ``name`` and ``os``. These have
 been replaced by ``instance_name`` and ``os_type`` to match the
 underlying opcode. The old names can still be used.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_CREATE
+
+
+.. _rapi-res-instances-instance_name:
 
 ``/2/instances/[instance_name]``
 ++++++++++++++++++++++++++++++++
 
 Instance-specific resource.
 
-It supports the following commands: ``GET``, ``DELETE``.
+.. rapi_resource_details:: /2/instances/[instance_name]
+
+
+.. _rapi-res-instances-instance_name+get:
 
 ``GET``
 ~~~~~~~
@@ -648,6 +1132,11 @@ It supports the following commands: ``GET``, ``DELETE``.
 Returns information about an instance, similar to the bulk output from
 the instance list.
 
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.I_FIELDS))`.
+
+
+.. _rapi-res-instances-instance_name+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -655,11 +1144,20 @@ Deletes an instance.
 
 It supports the ``dry-run`` argument.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_REMOVE
+
+
+.. _rapi-res-instances-instance_name-info:
 
 ``/2/instances/[instance_name]/info``
 +++++++++++++++++++++++++++++++++++++++
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/instances/[instance_name]/info
+
+
+.. _rapi-res-instances-instance_name-info+get:
 
 ``GET``
 ~~~~~~~
@@ -669,13 +1167,22 @@ Requests detailed information about the instance. An optional parameter,
 configuration without querying the instance's nodes. The result will be
 a job id.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_QUERY_DATA
+
+
+.. _rapi-res-instances-instance_name-reboot:
 
 ``/2/instances/[instance_name]/reboot``
 +++++++++++++++++++++++++++++++++++++++
 
 Reboots URI for an instance.
 
-It supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/instances/[instance_name]/reboot
+
+
+.. _rapi-res-instances-instance_name-reboot+post:
 
 ``POST``
 ~~~~~~~~
@@ -697,13 +1204,22 @@ instance even if secondary disks are failing.
 
 It supports the ``dry-run`` argument.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_REBOOT
+
+
+.. _rapi-res-instances-instance_name-shutdown:
 
 ``/2/instances/[instance_name]/shutdown``
 +++++++++++++++++++++++++++++++++++++++++
 
 Instance shutdown URI.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/shutdown
+
+
+.. _rapi-res-instances-instance_name-shutdown+put:
 
 ``PUT``
 ~~~~~~~
@@ -715,13 +1231,22 @@ It supports the ``dry-run`` argument.
 .. opcode_params:: OP_INSTANCE_SHUTDOWN
    :exclude: instance_name, dry_run
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_SHUTDOWN
+
+
+.. _rapi-res-instances-instance_name-startup:
 
 ``/2/instances/[instance_name]/startup``
 ++++++++++++++++++++++++++++++++++++++++
 
 Instance startup URI.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/startup
+
+
+.. _rapi-res-instances-instance_name-startup+put:
 
 ``PUT``
 ~~~~~~~
@@ -733,12 +1258,22 @@ instance even if secondary disks are failing.
 
 It supports the ``dry-run`` argument.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_STARTUP
+
+
+.. _rapi-res-instances-instance_name-reinstall:
+
 ``/2/instances/[instance_name]/reinstall``
 ++++++++++++++++++++++++++++++++++++++++++++++
 
 Installs the operating system again.
 
-It supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/instances/[instance_name]/reinstall
+
+
+.. _rapi-res-instances-instance_name-reinstall+post:
 
 ``POST``
 ~~~~~~~~
@@ -759,34 +1294,47 @@ parameters ``os`` (OS template name) and ``nostartup`` (bool). New
 clients should use the body parameters.
 
 
+.. _rapi-res-instances-instance_name-replace-disks:
+
 ``/2/instances/[instance_name]/replace-disks``
 ++++++++++++++++++++++++++++++++++++++++++++++
 
 Replaces disks on an instance.
 
-It supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/instances/[instance_name]/replace-disks
+
+
+.. _rapi-res-instances-instance_name-replace-disks+post:
 
 ``POST``
 ~~~~~~~~
 
-Takes the parameters ``mode`` (one of ``replace_on_primary``,
-``replace_on_secondary``, ``replace_new_secondary`` or
-``replace_auto``), ``disks`` (comma separated list of disk indexes),
-``remote_node`` and ``iallocator``.
+Returns a job ID.
+
+Body parameters:
+
+.. opcode_params:: OP_INSTANCE_REPLACE_DISKS
+   :exclude: instance_name
+
+Ganeti 2.4 and below used query parameters. Those are deprecated and
+should no longer be used.
 
-Either ``remote_node`` or ``iallocator`` needs to be defined when using
-``mode=replace_new_secondary``.
+Job result:
 
-``mode`` is a mandatory parameter. ``replace_auto`` tries to determine
-the broken disk(s) on its own and replacing it.
+.. opcode_result:: OP_INSTANCE_REPLACE_DISKS
 
 
+.. _rapi-res-instances-instance_name-activate-disks:
+
 ``/2/instances/[instance_name]/activate-disks``
 +++++++++++++++++++++++++++++++++++++++++++++++
 
 Activate disks on an instance.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/activate-disks
+
+
+.. _rapi-res-instances-instance_name-activate-disks+put:
 
 ``PUT``
 ~~~~~~~
@@ -794,26 +1342,71 @@ It supports the following commands: ``PUT``.
 Takes the bool parameter ``ignore_size``. When set ignore the recorded
 size (useful for forcing activation when recorded size is wrong).
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_ACTIVATE_DISKS
+
+
+.. _rapi-res-instances-instance_name-deactivate-disks:
 
 ``/2/instances/[instance_name]/deactivate-disks``
 +++++++++++++++++++++++++++++++++++++++++++++++++
 
 Deactivate disks on an instance.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/deactivate-disks
+
+
+.. _rapi-res-instances-instance_name-deactivate-disks+put:
 
 ``PUT``
 ~~~~~~~
 
 Takes no parameters.
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_DEACTIVATE_DISKS
+
+
+.. _rapi-res-instances-instance_name-recreate-disks:
+
+``/2/instances/[instance_name]/recreate-disks``
++++++++++++++++++++++++++++++++++++++++++++++++++
+
+Recreate disks of an instance.
+
+.. rapi_resource_details:: /2/instances/[instance_name]/recreate-disks
+
+
+.. _rapi-res-instances-instance_name-recreate-disks+post:
+
+``POST``
+~~~~~~~~
+
+Returns a job ID.
+
+Body parameters:
+
+.. opcode_params:: OP_INSTANCE_RECREATE_DISKS
+   :exclude: instance_name
+
+Job result:
+
+.. opcode_result:: OP_INSTANCE_RECREATE_DISKS
+
+
+.. _rapi-res-instances-instance_name-disk-disk_index-grow:
 
 ``/2/instances/[instance_name]/disk/[disk_index]/grow``
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 
 Grows one disk of an instance.
 
-Supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/instances/[instance_name]/disk/[disk_index]/grow
+
+
+.. _rapi-res-instances-instance_name-disk-disk_index-grow+post:
 
 ``POST``
 ~~~~~~~~
@@ -825,26 +1418,44 @@ Body parameters:
 .. opcode_params:: OP_INSTANCE_GROW_DISK
    :exclude: instance_name, disk
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_GROW_DISK
+
+
+.. _rapi-res-instances-instance_name-prepare-export:
 
 ``/2/instances/[instance_name]/prepare-export``
 +++++++++++++++++++++++++++++++++++++++++++++++++
 
 Prepares an export of an instance.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/prepare-export
+
+
+.. _rapi-res-instances-instance_name-prepare-export+put:
 
 ``PUT``
 ~~~~~~~
 
 Takes one parameter, ``mode``, for the export mode. Returns a job ID.
 
+Job result:
+
+.. opcode_result:: OP_BACKUP_PREPARE
+
+
+.. _rapi-res-instances-instance_name-export:
 
 ``/2/instances/[instance_name]/export``
 +++++++++++++++++++++++++++++++++++++++++++++++++
 
 Exports an instance.
 
-It supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/export
+
+
+.. _rapi-res-instances-instance_name-export+put:
 
 ``PUT``
 ~~~~~~~
@@ -857,13 +1468,22 @@ Body parameters:
    :exclude: instance_name
    :alias: target_node=destination
 
+Job result:
+
+.. opcode_result:: OP_BACKUP_EXPORT
+
+
+.. _rapi-res-instances-instance_name-migrate:
 
 ``/2/instances/[instance_name]/migrate``
 ++++++++++++++++++++++++++++++++++++++++
 
 Migrates an instance.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/migrate
+
+
+.. _rapi-res-instances-instance_name-migrate+put:
 
 ``PUT``
 ~~~~~~~
@@ -875,13 +1495,22 @@ Body parameters:
 .. opcode_params:: OP_INSTANCE_MIGRATE
    :exclude: instance_name, live
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_MIGRATE
+
+
+.. _rapi-res-instances-instance_name-failover:
 
 ``/2/instances/[instance_name]/failover``
 +++++++++++++++++++++++++++++++++++++++++
 
 Does a failover of an instance.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/failover
+
+
+.. _rapi-res-instances-instance_name-failover+put:
 
 ``PUT``
 ~~~~~~~
@@ -893,13 +1522,22 @@ Body parameters:
 .. opcode_params:: OP_INSTANCE_FAILOVER
    :exclude: instance_name
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_FAILOVER
+
+
+.. _rapi-res-instances-instance_name-rename:
 
 ``/2/instances/[instance_name]/rename``
 ++++++++++++++++++++++++++++++++++++++++
 
 Renames an instance.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/rename
+
+
+.. _rapi-res-instances-instance_name-rename+put:
 
 ``PUT``
 ~~~~~~~
@@ -911,13 +1549,22 @@ Body parameters:
 .. opcode_params:: OP_INSTANCE_RENAME
    :exclude: instance_name
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_RENAME
+
+
+.. _rapi-res-instances-instance_name-modify:
 
 ``/2/instances/[instance_name]/modify``
 ++++++++++++++++++++++++++++++++++++++++
 
 Modifies an instance.
 
-Supports the following commands: ``PUT``.
+.. rapi_resource_details:: /2/instances/[instance_name]/modify
+
+
+.. _rapi-res-instances-instance_name-modify+put:
 
 ``PUT``
 ~~~~~~~
@@ -929,13 +1576,22 @@ Body parameters:
 .. opcode_params:: OP_INSTANCE_SET_PARAMS
    :exclude: instance_name
 
+Job result:
+
+.. opcode_result:: OP_INSTANCE_SET_PARAMS
+
+
+.. _rapi-res-instances-instance_name-console:
 
 ``/2/instances/[instance_name]/console``
 ++++++++++++++++++++++++++++++++++++++++
 
 Request information for connecting to instance's console.
 
-Supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/instances/[instance_name]/console
+
+
+.. _rapi-res-instances-instance_name-console+get:
 
 ``GET``
 ~~~~~~~
@@ -943,30 +1599,62 @@ Supports the following commands: ``GET``.
 Returns a dictionary containing information about the instance's
 console. Contained keys:
 
+.. pyassert::
+
+   constants.CONS_ALL == frozenset([
+     constants.CONS_MESSAGE,
+     constants.CONS_SSH,
+     constants.CONS_VNC,
+     constants.CONS_SPICE,
+     ])
+
+.. pyassert::
+
+  frozenset(objects.InstanceConsole.GetAllSlots()) == frozenset([
+    "command",
+    "display",
+    "host",
+    "instance",
+    "kind",
+    "message",
+    "port",
+    "user",
+    ])
+
+
 ``instance``
-  Instance name.
+  Instance name
 ``kind``
-  Console type, one of ``ssh``, ``vnc`` or ``msg``.
+  Console type, one of :pyeval:`constants.CONS_SSH`,
+  :pyeval:`constants.CONS_VNC`, :pyeval:`constants.CONS_SPICE`
+  or :pyeval:`constants.CONS_MESSAGE`
 ``message``
-  Message to display (``msg`` type only).
+  Message to display (:pyeval:`constants.CONS_MESSAGE` type only)
 ``host``
-  Host to connect to (``ssh`` and ``vnc`` only).
+  Host to connect to (:pyeval:`constants.CONS_SSH`,
+  :pyeval:`constants.CONS_VNC` or :pyeval:`constants.CONS_SPICE` only)
 ``port``
-  TCP port to connect to (``vnc`` only).
+  TCP port to connect to (:pyeval:`constants.CONS_VNC` or
+  :pyeval:`constants.CONS_SPICE` only)
 ``user``
-  Username to use (``ssh`` only).
+  Username to use (:pyeval:`constants.CONS_SSH` only)
 ``command``
-  Command to execute on machine (``ssh`` only)
+  Command to execute on machine (:pyeval:`constants.CONS_SSH` only)
 ``display``
-  VNC display number (``vnc`` only).
+  VNC display number (:pyeval:`constants.CONS_VNC` only)
 
 
+.. _rapi-res-instances-instance_name-tags:
+
 ``/2/instances/[instance_name]/tags``
 +++++++++++++++++++++++++++++++++++++
 
 Manages per-instance tags.
 
-It supports the following commands: ``GET``, ``PUT``, ``DELETE``.
+.. rapi_resource_details:: /2/instances/[instance_name]/tags
+
+
+.. _rapi-res-instances-instance_name-tags+get:
 
 ``GET``
 ~~~~~~~
@@ -977,6 +1665,9 @@ Example::
 
     ["tag1", "tag2", "tag3"]
 
+
+.. _rapi-res-instances-instance_name-tags+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -988,6 +1679,8 @@ result will be a job id.
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-instances-instance_name-tags+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -1001,12 +1694,17 @@ to URI like::
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-jobs:
+
 ``/2/jobs``
 +++++++++++
 
 The ``/2/jobs`` resource.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/jobs
+
+
+.. _rapi-res-jobs+get:
 
 ``GET``
 ~~~~~~~
@@ -1019,20 +1717,28 @@ If the optional bool *bulk* argument is provided and set to a true value
 (i.e. ``?bulk=1``), the output contains detailed information about jobs
 as a list.
 
+Returned fields for bulk requests (unlike other bulk requests, these
+fields are not the same as for per-job requests):
+:pyeval:`utils.CommaJoin(sorted(rlib2.J_FIELDS_BULK))`.
+
+
+.. _rapi-res-jobs-job_id:
+
 ``/2/jobs/[job_id]``
 ++++++++++++++++++++
 
-
 Individual job URI.
 
-It supports the following commands: ``GET``, ``DELETE``.
+.. rapi_resource_details:: /2/jobs/[job_id]
+
+
+.. _rapi-res-jobs-job_id+get:
 
 ``GET``
 ~~~~~~~
 
-Returns a job status.
-
-Returns: a dictionary with job parameters.
+Returns a dictionary with job parameters, containing the fields
+:pyeval:`utils.CommaJoin(sorted(rlib2.J_FIELDS))`.
 
 The result includes:
 
@@ -1065,7 +1771,7 @@ classification:
    errors.ECODE_ALL == set([errors.ECODE_RESOLVER, errors.ECODE_NORES,
      errors.ECODE_INVAL, errors.ECODE_STATE, errors.ECODE_NOENT,
      errors.ECODE_EXISTS, errors.ECODE_NOTUNIQUE, errors.ECODE_FAULT,
-     errors.ECODE_ENVIRON])
+     errors.ECODE_ENVIRON, errors.ECODE_TEMP_NORES])
 
 :pyeval:`errors.ECODE_RESOLVER`
   Resolver errors. This usually means that a name doesn't exist in DNS,
@@ -1077,6 +1783,10 @@ classification:
   etc.). If the resources on the cluster increase, the operation might
   succeed.
 
+:pyeval:`errors.ECODE_TEMP_NORES`
+  Simliar to :pyeval:`errors.ECODE_NORES`, but indicating the operation
+  should be attempted again after some time.
+
 :pyeval:`errors.ECODE_INVAL`
   Wrong arguments (at syntax level). The operation will not ever be
   accepted unless the arguments change.
@@ -1110,15 +1820,24 @@ Note that in the above list, by entity we refer to a node or instance,
 while by a resource we refer to an instance's disk, or NIC, etc.
 
 
+.. _rapi-res-jobs-job_id+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
 Cancel a not-yet-started job.
 
 
+.. _rapi-res-jobs-job_id-wait:
+
 ``/2/jobs/[job_id]/wait``
 +++++++++++++++++++++++++
 
+.. rapi_resource_details:: /2/jobs/[job_id]/wait
+
+
+.. _rapi-res-jobs-job_id-wait+get:
+
 ``GET``
 ~~~~~~~
 
@@ -1126,25 +1845,30 @@ Waits for changes on a job. Takes the following body parameters in a
 dict:
 
 ``fields``
-  The job fields on which to watch for changes.
+  The job fields on which to watch for changes
 
 ``previous_job_info``
-  Previously received field values or None if not yet available.
+  Previously received field values or None if not yet available
 
 ``previous_log_serial``
   Highest log serial number received so far or None if not yet
-  available.
+  available
 
 Returns None if no changes have been detected and a dict with two keys,
 ``job_info`` and ``log_entries`` otherwise.
 
 
+.. _rapi-res-nodes:
+
 ``/2/nodes``
 ++++++++++++
 
 Nodes resource.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/nodes
+
+
+.. _rapi-res-nodes+get:
 
 ``GET``
 ~~~~~~~
@@ -1164,9 +1888,11 @@ Example::
       }
     ]
 
-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.
+If the optional bool *bulk* argument is provided and set to a true value
+(i.e ``?bulk=1``), the output contains detailed information about nodes
+as a list.
+
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.N_FIELDS))`.
 
 Example::
 
@@ -1181,24 +1907,65 @@ Example::
         "dtotal": 5246208,
         "sinst_cnt": 2,
         "dfree": 5171712,
-        "offline": false
+        "offline": false,
+        …
       },
-      ...
+      …
     ]
 
+
+.. _rapi-res-nodes-node_name:
+
 ``/2/nodes/[node_name]``
 +++++++++++++++++++++++++++++++++
 
 Returns information about a node.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/nodes/[node_name]
+
+
+.. _rapi-res-nodes-node_name+get:
+
+``GET``
+~~~~~~~
+
+Returned fields: :pyeval:`utils.CommaJoin(sorted(rlib2.N_FIELDS))`.
+
+
+
+.. _rapi-res-nodes-node_name-powercycle:
+
+``/2/nodes/[node_name]/powercycle``
++++++++++++++++++++++++++++++++++++
+
+Powercycles a node.
+
+.. rapi_resource_details:: /2/nodes/[node_name]/powercycle
+
+
+.. _rapi-res-nodes-node_name-powercycle+post:
+
+``POST``
+~~~~~~~~
+
+Returns a job ID.
+
+Job result:
+
+.. opcode_result:: OP_NODE_POWERCYCLE
+
+
+.. _rapi-res-nodes-node_name-evacuate:
 
 ``/2/nodes/[node_name]/evacuate``
 +++++++++++++++++++++++++++++++++
 
 Evacuates instances off a node.
 
-It supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/nodes/[node_name]/evacuate
+
+
+.. _rapi-res-nodes-node_name-evacuate+post:
 
 ``POST``
 ~~~~~~~~
@@ -1215,13 +1982,22 @@ Up to and including Ganeti 2.4 query arguments were used. Those are no
 longer supported. The new request can be detected by the presence of the
 :pyeval:`rlib2._NODE_EVAC_RES1` feature string.
 
+Job result:
+
+.. opcode_result:: OP_NODE_EVACUATE
+
+
+.. _rapi-res-nodes-node_name-migrate:
 
 ``/2/nodes/[node_name]/migrate``
 +++++++++++++++++++++++++++++++++
 
 Migrates all primary instances from a node.
 
-It supports the following commands: ``POST``.
+.. rapi_resource_details:: /2/nodes/[node_name]/migrate
+
+
+.. _rapi-res-nodes-node_name-migrate+post:
 
 ``POST``
 ~~~~~~~~
@@ -1236,13 +2012,19 @@ The query arguments used up to and including Ganeti 2.4 are deprecated
 and should no longer be used. The new request format can be detected by
 the presence of the :pyeval:`rlib2._NODE_MIGRATE_REQV1` feature string.
 
+Job result:
+
+.. opcode_result:: OP_NODE_MIGRATE
+
+
+.. _rapi-res-nodes-node_name-role:
 
 ``/2/nodes/[node_name]/role``
 +++++++++++++++++++++++++++++
 
 Manages node role.
 
-It supports the following commands: ``GET``, ``PUT``.
+.. rapi_resource_details:: /2/nodes/[node_name]/role
 
 The role is always one of the following:
 
@@ -1255,6 +2037,9 @@ Note that the 'master' role is a special, and currently it can't be
 modified via RAPI, only via the command line (``gnt-cluster
 master-failover``).
 
+
+.. _rapi-res-nodes-node_name-role+get:
+
 ``GET``
 ~~~~~~~
 
@@ -1264,6 +2049,9 @@ Example::
 
     "master-candidate"
 
+
+.. _rapi-res-nodes-node_name-role+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1274,15 +2062,55 @@ be a job id.
 
 It supports the bool ``force`` argument.
 
+Job result:
+
+.. opcode_result:: OP_NODE_SET_PARAMS
+
+
+.. _rapi-res-nodes-node_name-modify:
+
+``/2/nodes/[node_name]/modify``
++++++++++++++++++++++++++++++++
+
+Modifies the parameters of a node.
+
+.. rapi_resource_details:: /2/nodes/[node_name]/modify
+
+
+.. _rapi-res-nodes-node_name-modify+post:
+
+``POST``
+~~~~~~~~
+
+Returns a job ID.
+
+Body parameters:
+
+.. opcode_params:: OP_NODE_SET_PARAMS
+   :exclude: node_name
+
+Job result:
+
+.. opcode_result:: OP_NODE_SET_PARAMS
+
+
+.. _rapi-res-nodes-node_name-storage:
+
 ``/2/nodes/[node_name]/storage``
 ++++++++++++++++++++++++++++++++
 
 Manages storage units on the node.
 
+.. rapi_resource_details:: /2/nodes/[node_name]/storage
+
+
+.. _rapi-res-nodes-node_name-storage+get:
+
 ``GET``
 ~~~~~~~
 
-.. pyassert::
+FIXME: enable ".. pyassert::" again when all storage types are
+implemented::
 
    constants.VALID_STORAGE_TYPES == set([constants.ST_FILE,
                                          constants.ST_LVM_PV,
@@ -1294,11 +2122,19 @@ Requests a list of storage units on a node. Requires the parameters
 ``output_fields``. The result will be a job id, using which the result
 can be retrieved.
 
+
+.. _rapi-res-nodes-node_name-storage-modify:
+
 ``/2/nodes/[node_name]/storage/modify``
 +++++++++++++++++++++++++++++++++++++++
 
 Modifies storage units on the node.
 
+.. rapi_resource_details:: /2/nodes/[node_name]/storage/modify
+
+
+.. _rapi-res-nodes-node_name-storage-modify+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1309,11 +2145,23 @@ and ``name`` (name of the storage unit).  Parameters can be passed
 additionally. Currently only :pyeval:`constants.SF_ALLOCATABLE` (bool)
 is supported. The result will be a job id.
 
+Job result:
+
+.. opcode_result:: OP_NODE_MODIFY_STORAGE
+
+
+.. _rapi-res-nodes-node_name-storage-repair:
+
 ``/2/nodes/[node_name]/storage/repair``
 +++++++++++++++++++++++++++++++++++++++
 
 Repairs a storage unit on the node.
 
+.. rapi_resource_details:: /2/nodes/[node_name]/storage/repair
+
+
+.. _rapi-res-nodes-node_name-storage-repair+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1328,12 +2176,22 @@ Repairs a storage unit on the node. Requires the parameters
 repaired) and ``name`` (name of the storage unit). The result will be a
 job id.
 
+Job result:
+
+.. opcode_result:: OP_REPAIR_NODE_STORAGE
+
+
+.. _rapi-res-nodes-node_name-tags:
+
 ``/2/nodes/[node_name]/tags``
 +++++++++++++++++++++++++++++
 
 Manages per-node tags.
 
-It supports the following commands: ``GET``, ``PUT``, ``DELETE``.
+.. rapi_resource_details:: /2/nodes/[node_name]/tags
+
+
+.. _rapi-res-nodes-node_name-tags+get:
 
 ``GET``
 ~~~~~~~
@@ -1344,6 +2202,9 @@ Example::
 
     ["tag1", "tag2", "tag3"]
 
+
+.. _rapi-res-nodes-node_name-tags+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1354,6 +2215,9 @@ will be a job id.
 
 It supports the ``dry-run`` argument.
 
+
+.. _rapi-res-nodes-node_name-tags+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -1367,6 +2231,8 @@ to URI like::
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-query-resource:
+
 ``/2/query/[resource]``
 +++++++++++++++++++++++
 
@@ -1375,7 +2241,10 @@ pages and using ``/2/query/[resource]/fields``. The resource is one of
 :pyeval:`utils.CommaJoin(constants.QR_VIA_RAPI)`. See the :doc:`query2
 design document <design-query2>` for more details.
 
-Supports the following commands: ``GET``, ``PUT``.
+.. rapi_resource_details:: /2/query/[resource]
+
+
+.. _rapi-res-query-resource+get:
 
 ``GET``
 ~~~~~~~
@@ -1384,6 +2253,9 @@ Returns list of included fields and actual data. Takes a query parameter
 named "fields", containing a comma-separated list of field names. Does
 not support filtering.
 
+
+.. _rapi-res-query-resource+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1394,6 +2266,8 @@ be given and must be either ``null`` or a list containing filter
 operators.
 
 
+.. _rapi-res-query-resource-fields:
+
 ``/2/query/[resource]/fields``
 ++++++++++++++++++++++++++++++
 
@@ -1401,7 +2275,10 @@ Request list of available fields for a resource. The resource is one of
 :pyeval:`utils.CommaJoin(constants.QR_VIA_RAPI)`. See the
 :doc:`query2 design document <design-query2>` for more details.
 
-Supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/query/[resource]/fields
+
+
+.. _rapi-res-query-resource-fields+get:
 
 ``GET``
 ~~~~~~~
@@ -1411,12 +2288,17 @@ optional query parameter named "fields", containing a comma-separated
 list of field names.
 
 
+.. _rapi-res-os:
+
 ``/2/os``
 +++++++++
 
 OS resource.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /2/os
+
+
+.. _rapi-res-os+get:
 
 ``GET``
 ~~~~~~~
@@ -1430,12 +2312,18 @@ Example::
 
     ["debian-etch"]
 
+
+.. _rapi-res-tags:
+
 ``/2/tags``
 +++++++++++
 
 Manages cluster tags.
 
-It supports the following commands: ``GET``, ``PUT``, ``DELETE``.
+.. rapi_resource_details:: /2/tags
+
+
+.. _rapi-res-tags+get:
 
 ``GET``
 ~~~~~~~
@@ -1446,6 +2334,9 @@ Example::
 
     ["tag1", "tag2", "tag3"]
 
+
+.. _rapi-res-tags+put:
+
 ``PUT``
 ~~~~~~~
 
@@ -1457,6 +2348,8 @@ will be a job id.
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-tags+delete:
+
 ``DELETE``
 ~~~~~~~~~~
 
@@ -1470,6 +2363,8 @@ to URI like::
 It supports the ``dry-run`` argument.
 
 
+.. _rapi-res-version:
+
 ``/version``
 ++++++++++++
 
@@ -1478,7 +2373,10 @@ The version resource.
 This resource should be used to determine the remote API version and to
 adapt clients accordingly.
 
-It supports the following commands: ``GET``.
+.. rapi_resource_details:: /version
+
+
+.. _rapi-res-version+get:
 
 ``GET``
 ~~~~~~~
@@ -1486,6 +2384,18 @@ It supports the following commands: ``GET``.
 Returns the remote API version. Ganeti 1.2 returned ``1`` and Ganeti 2.0
 returns ``2``.
 
+
+.. _rapi-access-permissions:
+
+Access permissions
+------------------
+
+The following list describes the access permissions required for each
+resource. See :ref:`rapi-users` for more details.
+
+.. rapi_access_table::
+
+
 .. vim: set textwidth=72 :
 .. Local Variables:
 .. mode: rst