diff --git a/daemons/ganeti-rapi b/daemons/ganeti-rapi
index c8e8953d39714be721320f5a9d4c1ca68557bc17..78da6dccdfb3404eb3c8bdacf6301fc1a4d78188 100755
--- a/daemons/ganeti-rapi
+++ b/daemons/ganeti-rapi
@@ -22,15 +22,16 @@
 """
 
 import glob
+import logging
 import optparse
 import sys
 import os
 import signal
 
+from ganeti import logger
 from ganeti import constants
 from ganeti import errors
 from ganeti import http
-from ganeti import rpc
 from ganeti import ssconf
 from ganeti import utils
 from ganeti.rapi import connector
@@ -49,7 +50,7 @@ class RESTRequestHandler(http.HTTPRequestHandler):
 
     """
     (HandlerClass, items, args) = self._resmap.getController(self.path)
-    handler = HandlerClass(self, items, args)
+    handler = HandlerClass(self, items, args, self.post_data)
 
     command = self.command.upper()
     try:
@@ -58,7 +59,11 @@ class RESTRequestHandler(http.HTTPRequestHandler):
       raise http.HTTPBadRequest()
 
     try:
-      result = fn()
+      try:
+        result = fn()
+      except:
+        logging.exception("Error while handling the %s request", command)
+        raise
 
     except errors.OpPrereqError, err:
       # TODO: "Not found" is not always the correct error. Ganeti's core must
@@ -135,6 +140,9 @@ def main():
   if options.fork:
     utils.Daemonize(logfile=constants.LOG_RAPISERVER)
 
+  logger.SetupLogging(constants.LOG_RAPISERVER, debug=options.debug,
+                     stderr_logging=not options.fork)
+
   utils.WritePidFile(constants.RAPI_PID)
 
   log_fd = open(constants.LOG_RAPIACCESS, 'a')
@@ -155,4 +163,5 @@ def main():
 
 
 if __name__ == '__main__':
+  
   main()
diff --git a/lib/rapi/baserlib.py b/lib/rapi/baserlib.py
index 080fcd8599b149a6530c134da629aada57c5b2c8..0de15c9325a9331aa3892738b7f87548010d4c60 100644
--- a/lib/rapi/baserlib.py
+++ b/lib/rapi/baserlib.py
@@ -26,6 +26,8 @@
 import ganeti.cli
 import ganeti.opcodes
 
+from ganeti import luxi
+
 
 def BuildUriList(ids, uri_format, uri_fields=("name", "uri")):
   """Builds a URI list as used by index resources.
@@ -89,6 +91,19 @@ def _Tags_GET(kind, name=None):
   return list(tags)
 
 
+def _Tags_POST(kind, tags, name=None):
+  """Helper function to set tags.
+
+  """
+  if name is None:
+    # Do not cause "missing parameter" error, which happens if a parameter
+    # is None.
+    name = ""
+  cl = luxi.Client()
+  return cl.SubmitJob([ganeti.opcodes.OpAddTags(kind=kind, name=name,
+                                                tags=tags)])
+
+
 def MapBulkFields(itemslist, fields):
   """Map value to field name in to one dictionary.
 
@@ -110,7 +125,7 @@ class R_Generic(object):
   """Generic class for resources.
 
   """
-  def __init__(self, request, items, queryargs):
+  def __init__(self, request, items, queryargs, post_data):
     """Generic resource constructor.
 
     Args:
@@ -122,3 +137,4 @@ class R_Generic(object):
     self.request = request
     self.items = items
     self.queryargs = queryargs
+    self.post_data = post_data
diff --git a/lib/rapi/connector.py b/lib/rapi/connector.py
index a80250cce11793636fccce7a7c9552bb0639d0c1..1b0fea482b9fc66b834f2f378bd7269082072c33 100644
--- a/lib/rapi/connector.py
+++ b/lib/rapi/connector.py
@@ -137,5 +137,8 @@ CONNECTOR.update({
 
   "/2/jobs": rlib2.R_2_jobs,
   "/2/nodes": rlib2.R_2_nodes,
+  "/2/instances": rlib2.R_2_instances,
+  re.compile(r'^/2/instances/([\w\._-]+)$'): rlib1.R_instances_name,
+  re.compile(r'^/2/instances/([\w\._-]+)/tags$'): rlib2.R_2_instances_name_tags,
   re.compile(r'/2/jobs/(%s)$' % constants.JOB_ID_TEMPLATE): rlib2.R_2_jobs_id,
   })
diff --git a/lib/rapi/rlib2.py b/lib/rapi/rlib2.py
index 0a777cc02e493c65ca93a5191fa80f2727bb4391..9c4863b409724d205b6915fe5fd9f5f0de5270ea 100644
--- a/lib/rapi/rlib2.py
+++ b/lib/rapi/rlib2.py
@@ -129,5 +129,97 @@ class R_2_nodes(baserlib.R_Generic):
       result = ganeti.cli.SubmitOpCode(op)
       return baserlib.MapBulkFields(result, N_FIELDS)
 
-    return baserlib.BuildUriList(nodeslist, "/nodes/%s",
+    return baserlib.BuildUriList(nodeslist, "/2/nodes/%s",
                                  uri_fields=("id", "uri"))
+
+
+class R_2_instances(baserlib.R_Generic):
+  """/2/instances resource.
+
+  """
+  DOC_URI = "/2/instances"
+
+
+  def GET(self):
+    """Returns a list of all available instances.
+
+    Returns:
+       A dictionary with 'name' and 'uri' keys for each of them.
+
+    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",
+           "bridge": "xen-br0",
+           "name": "web.example.com",
+           "tags": ["tag1", "tag2"],
+           "admin_ram": 512,
+           "sda_size": 20480,
+           "pnode": "node1.example.com",
+           "mac": "01:23:45:67:89:01",
+           "sdb_size": 4096,
+           "snodes": ["node2.example.com"],
+           "disk_template": "drbd",
+           "ip": null,
+           "admin_state": true,
+           "os": "debian-etch",
+           "vcpus": 2,
+           "oper_state": true
+        },
+        ...
+    ]
+
+    """
+    op = ganeti.opcodes.OpQueryInstances(output_fields=["name"], names=[])
+    instanceslist = baserlib.ExtractField(ganeti.cli.SubmitOpCode(op), 0)
+
+    if 'bulk' in self.queryargs:
+      op = ganeti.opcodes.OpQueryInstances(output_fields=I_FIELDS,
+                                           names=instanceslist)
+      result = ganeti.cli.SubmitOpCode(op)
+      return baserlib.MapBulkFields(result, I_FIELDS)
+
+
+    else:
+      return baserlib.BuildUriList(instanceslist, "/2/instances/%s",
+                                   uri_fields=("id", "uri"))
+
+
+class R_2_instances_name_tags(baserlib.R_Generic):
+  """/2/instances/[instance_name]/tags resource.
+
+  Manages per-instance tags.
+
+  """
+  DOC_URI = "/2/instances/[instance_name]/tags"
+
+  def GET(self):
+    """Returns a list of instance tags.
+
+    Example: ["tag1", "tag2", "tag3"]
+
+    """
+    return baserlib._Tags_GET(constants.TAG_INSTANCE, name=self.items[0])
+
+  def POST(self):
+    """Add a set of tags to the instance.
+
+    The reqest as a list of strings should be POST to this URI. And you'll have
+    back a job id.
+
+    """
+    return baserlib._Tags_POST(constants.TAG_INSTANCE,
+                               self.post_data, name=self.items[0])
diff --git a/test/ganeti.rapi.resources_unittest.py b/test/ganeti.rapi.resources_unittest.py
index a2401b8c505eab8ce358953d47034532498d8e83..c75cf171e7044962090442e4ceb93308173b0ed9 100755
--- a/test/ganeti.rapi.resources_unittest.py
+++ b/test/ganeti.rapi.resources_unittest.py
@@ -28,8 +28,8 @@ import tempfile
 from ganeti import errors
 from ganeti import http
 
-from ganeti.rapi import connector
-from ganeti.rapi import rlib1
+from ganeti.rapi import connector 
+from ganeti.rapi import rlib1 
 
 
 class MapperTests(unittest.TestCase):
@@ -70,7 +70,7 @@ class R_RootTests(unittest.TestCase):
   """Testing for R_root class."""
 
   def setUp(self):
-    self.root = connector.R_root(None, None, None)
+    self.root = connector.R_root(None, None, None, None)
 
   def testGet(self):
     expected = [