diff --git a/doc/rapi.rst b/doc/rapi.rst
index 3f69a6bffb3ddb250c06cbcf9f8e76abba51a0ab..a10ae5e9091290dab91773ea879f484efc53e71b 100644
--- a/doc/rapi.rst
+++ b/doc/rapi.rst
@@ -332,6 +332,65 @@ features:
   Instance reinstall supports body parameters.
 
 
+``/2/groups``
++++++++++++++
+
+The groups resource.
+
+It supports the following commands: ``GET``.
+
+``GET``
+~~~~~~~
+
+Returns a list of all existing node groups.
+
+Example::
+
+    [
+      {
+        "name": "group1",
+        "uri": "\/2\/groups\/group1"
+      },
+      {
+        "name": "group2",
+        "uri": "\/2\/groups\/group2"
+      }
+    ]
+
+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.
+
+Example::
+
+    [
+      {
+        "name": "group1",
+        "node_cnt": 2,
+        "node_list": [
+          "node1.example.com",
+          "node2.example.com"
+        ],
+        "uuid": "0d7d407c-262e-49af-881a-6a430034bf43"
+      },
+      {
+        "name": "group2",
+        "node_cnt": 1,
+        "node_list": [
+          "node3.example.com"
+        ],
+        "uuid": "f5a277e7-68f9-44d3-a378-4b25ecb5df5c"
+      }
+    ]
+
+``/2/groups/[group_name]``
++++++++++++++++++++++++++++++++++
+
+Returns information about a node group.
+
+It supports the following commands: ``GET``.
+
+
 ``/2/instances``
 ++++++++++++++++
 
diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py
index 7650b2136047377fcc56c792ee99e1e654f8665a..f4015eeab58220e6b491554c07cb7d74097ab971 100644
--- a/lib/rapi/connector.py
+++ b/lib/rapi/connector.py
@@ -146,7 +146,8 @@ class R_2(baserlib.R_Generic):
     return baserlib.BuildUriList(_getResources("2"), "/2/%s")
 
 
-def GetHandlers(node_name_pattern, instance_name_pattern, job_id_pattern):
+def GetHandlers(node_name_pattern, instance_name_pattern,
+                group_name_pattern, job_id_pattern):
   """Returns all supported resources and their handlers.
 
   """
@@ -211,6 +212,10 @@ def GetHandlers(node_name_pattern, instance_name_pattern, job_id_pattern):
     re.compile(r'^/2/instances/(%s)/modify$' % instance_name_pattern):
       rlib2.R_2_instances_name_modify,
 
+    "/2/groups": rlib2.R_2_groups,
+    re.compile(r'^/2/groups/(%s)$' % group_name_pattern):
+      rlib2.R_2_groups_name,
+
     "/2/jobs": rlib2.R_2_jobs,
     re.compile(r"^/2/jobs/(%s)$" % job_id_pattern):
       rlib2.R_2_jobs_id,
@@ -225,5 +230,5 @@ def GetHandlers(node_name_pattern, instance_name_pattern, job_id_pattern):
     }
 
 
-CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN,
+CONNECTOR.update(GetHandlers(_NAME_PATTERN, _NAME_PATTERN, _NAME_PATTERN,
                              constants.JOB_ID_TEMPLATE))
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index e5f392816eb7d950b48186d3c3e2dceb55886f59..8f5dc38692995b3e735023a0c6dbd995ebc585c1 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -73,6 +73,10 @@ N_FIELDS = ["name", "offline", "master_candidate", "drained",
             "group.uuid",
             ] + _COMMON_FIELDS
 
+G_FIELDS = ["name", "uuid",
+            "node_cnt", "node_list",
+            ]
+
 _NR_DRAINED = "drained"
 _NR_MASTER_CANDIATE = "master-candidate"
 _NR_MASTER = "master"
@@ -526,6 +530,45 @@ class R_2_nodes_name_storage_repair(baserlib.R_Generic):
     return baserlib.SubmitJob([op])
 
 
+class R_2_groups(baserlib.R_Generic):
+  """/2/groups resource.
+
+  """
+  def GET(self):
+    """Returns a list of all node groups.
+
+    """
+    client = baserlib.GetClient()
+
+    if self.useBulk():
+      bulkdata = client.QueryGroups([], G_FIELDS, False)
+      return baserlib.MapBulkFields(bulkdata, G_FIELDS)
+    else:
+      data = client.QueryGroups([], ["name"], False)
+      groupnames = [row[0] for row in data]
+      return baserlib.BuildUriList(groupnames, "/2/groups/%s",
+                                   uri_fields=("name", "uri"))
+
+
+class R_2_groups_name(baserlib.R_Generic):
+  """/2/groups/[group_name] resources.
+
+  """
+  def GET(self):
+    """Send information about a node group.
+
+    """
+    group_name = self.items[0]
+    client = baserlib.GetClient()
+
+    result = baserlib.HandleItemQueryErrors(client.QueryGroups,
+                                            names=[group_name], fields=G_FIELDS,
+                                            use_locking=self.useLocking())
+
+    return baserlib.MapFields(G_FIELDS, result[0])
+
+
+
 def _ParseInstanceCreateRequestVersion1(data, dry_run):
   """Parses an instance creation request version 1.
 
diff --git a/test/docs_unittest.py b/test/docs_unittest.py
index 5be823b2d7149e214c793d293988b4f47c161a43..a2a33b8cf5bc0275c415a251b116a373743131c6 100755
--- a/test/docs_unittest.py
+++ b/test/docs_unittest.py
@@ -77,10 +77,12 @@ class TestDocs(unittest.TestCase):
 
     node_name = "[node_name]"
     instance_name = "[instance_name]"
+    group_name = "[group_name]"
     job_id = "[job_id]"
 
     resources = connector.GetHandlers(re.escape(node_name),
                                       re.escape(instance_name),
+                                      re.escape(group_name),
                                       re.escape(job_id))
 
     titles = []