diff --git a/configure.ac b/configure.ac
index 6c3fa4ac4749398d2ce5fc0e16bfb38a3eb48724..1a0f90c9873721b9e38712c0ae28f37cedc6f0d9 100644
--- a/configure.ac
+++ b/configure.ac
@@ -50,6 +50,17 @@ AC_ARG_WITH([os-search-path],
   [os_search_path="'/srv/ganeti/os'"])
 AC_SUBST(OS_SEARCH_PATH, $os_search_path)
 
+# --with-iallocator-search-path=...
+# do a bit of black sed magic to for quoting of the strings in the list
+AC_ARG_WITH([iallocator-search-path],
+  [AS_HELP_STRING([--with-iallocator-search-path=LIST],
+    [comma separated list of directories to]
+    [ search for instance allocators (default is $libdir/ganeti/iallocators)]
+  )],
+  [iallocator_search_path=`echo -n "$withval" | sed -e "s/\([[^,]]*\)/'\1'/g"`],
+  [iallocator_search_path="'$libdir/$PACKAGE_NAME/iallocators'"])
+AC_SUBST(IALLOCATOR_SEARCH_PATH, $iallocator_search_path)
+
 # --with-xen-kernel=...
 AC_ARG_WITH([xen-kernel],
   [AS_HELP_STRING([--with-xen-kernel=PATH],
diff --git a/doc/examples/dumb-allocator b/doc/examples/dumb-allocator
new file mode 100755
index 0000000000000000000000000000000000000000..f6d6560fd440b66fa3a981b79e513efacb95f1c8
--- /dev/null
+++ b/doc/examples/dumb-allocator
@@ -0,0 +1,100 @@
+#!/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.
+
+"""Simple first-fit allocator for ganeti instance allocation framework.
+
+This allocator just iterates over the nodes and selects the first one
+that fits in both memory and disk space, without any consideration for
+equal spread or VCPU oversubscription.
+
+"""
+import simplejson
+import sys
+
+
+def SelectNode(nodes, request, to_skip):
+  """Select a node for the given instance
+
+  """
+  disk_size = request["disk_space_total"]
+  selected = None
+  for nname, ninfo in nodes.iteritems():
+    if nname in to_skip:
+      continue
+    if request["memory"] > ninfo["free_memory"]:
+      continue
+    if disk_size > ninfo["free_disk"]:
+      continue
+    selected = nname
+    break
+  return selected
+
+
+def OutputError(text):
+  """Builds an error response with a given info message.
+
+  """
+  error = {
+    "success": False,
+    "info": text,
+    }
+  print simplejson.dumps(error, indent=2)
+  return 1
+
+
+def main():
+  """Main function.
+
+  """
+  if len(sys.argv) < 2:
+    print >> sys.stderr, "Usage: %s cluster.json" % (sys.argv[0])
+    return 1
+
+  data = simplejson.load(open(sys.argv[1]))
+
+  nodes =  data["nodes"]
+  request = data["request"]
+  req_type = request["type"]
+  if req_type != "allocate":
+    print >> sys.stderr, "Unsupported allocator mode '%s'" % req_type
+    return 1
+
+  npri = SelectNode(nodes, request, [])
+  if npri is None:
+    return OutputError("Can't find a suitable primary node")
+
+  result_nodes = [npri]
+  if request["disk_template"] == "drbd":
+    nsec = SelectNode(nodes, request, result_nodes)
+    if nsec is None:
+      return OutputError("Can't find a suitable secondary node (%s selected"
+                         " as primary)" % npri)
+    result_nodes.append(nsec)
+
+  result = {
+          "success": True,
+          "info": "Allocation successful",
+          "nodes": result_nodes,
+          }
+  print simplejson.dumps(result, indent=2)
+  return 0
+
+if __name__ == "__main__":
+    sys.exit(main())
diff --git a/lib/Makefile.am b/lib/Makefile.am
index 140ed3aa486f47db75fab8070d041494c728fea9..994b2a3940a9a5f3445ef275ef98cd221791f199 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -25,6 +25,7 @@ _autoconf.py: Makefile
 	  echo "XEN_KERNEL = '$(XEN_KERNEL)'"; \
 	  echo "XEN_INITRD = '$(XEN_INITRD)'"; \
 	  echo "FILE_STORAGE_DIR = '$(FILE_STORAGE_DIR)'"; \
+	  echo "IALLOCATOR_SEARCH_PATH = [$(IALLOCATOR_SEARCH_PATH)]"; \
 	} > $@
 
 pre-check: all
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 89a328306fa842605b3f22e41efd5d438c228c8c..c8816653cb58fa2b4a991eb972e270cbc20f4828 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -4650,7 +4650,7 @@ class LUTestDelay(NoHooksLU):
                                    " result: %s" % (node, node_result))
 
 
-def _AllocatorGetClusterData(cfg, sstore):
+def _IAllocatorGetClusterData(cfg, sstore):
   """Compute the generic allocator input data.
 
   This is the data that is independent of the actual operation.
@@ -4720,7 +4720,7 @@ def _AllocatorGetClusterData(cfg, sstore):
   return data
 
 
-def _AllocatorAddNewInstance(data, op):
+def _IAllocatorAddNewInstance(data, op):
   """Add new instance data to allocator structure.
 
   This in combination with _AllocatorGetClusterData will create the
@@ -4730,6 +4730,12 @@ def _AllocatorAddNewInstance(data, op):
   done.
 
   """
+  if len(op.disks) != 2:
+    raise errors.OpExecError("Only two-disk configurations supported")
+
+  disk_space = _ComputeDiskSize(op.disk_template,
+                                op.disks[0]["size"], op.disks[1]["size"])
+
   request = {
     "type": "allocate",
     "name": op.name,
@@ -4739,15 +4745,16 @@ def _AllocatorAddNewInstance(data, op):
     "vcpus": op.vcpus,
     "memory": op.mem_size,
     "disks": op.disks,
+    "disk_space_total": disk_space,
     "nics": op.nics,
     }
   data["request"] = request
 
 
-def _AllocatorAddRelocateInstance(data, op):
+def _IAllocatorAddRelocateInstance(data, op):
   """Add relocate instance data to allocator structure.
 
-  This in combination with _AllocatorGetClusterData will create the
+  This in combination with _IAllocatorGetClusterData will create the
   correct structure needed as input for the allocator.
 
   The checks for the completeness of the opcode must have already been
@@ -4761,6 +4768,29 @@ def _AllocatorAddRelocateInstance(data, op):
   data["request"] = request
 
 
+def _IAllocatorRun(name, data):
+  """Run an instance allocator and return the results.
+
+  """
+  alloc_script = utils.FindFile(name, constants.IALLOCATOR_SEARCH_PATH,
+                                os.path.isfile)
+  if alloc_script is None:
+    raise errors.OpExecError("Can't find allocator")
+
+  fd, fin_name = tempfile.mkstemp(prefix="ganeti-iallocator.")
+  try:
+    os.write(fd, data)
+    os.close(fd)
+    result = utils.RunCmd([alloc_script, fin_name])
+    if result.failed:
+      raise errors.OpExecError("Instance allocator call failed: %s,"
+                               " output: %s" %
+                               (result.fail_reason, result.stdout))
+  finally:
+    os.unlink(fin_name)
+  return result.stdout
+
+
 class LUTestAllocator(NoHooksLU):
   """Run allocator tests.
 
@@ -4775,7 +4805,7 @@ class LUTestAllocator(NoHooksLU):
     This checks the opcode parameters depending on the director and mode test.
 
     """
-    if self.op.mode == constants.ALF_MODE_ALLOC:
+    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
       for attr in ["name", "mem_size", "disks", "disk_template",
                    "os", "tags", "nics", "vcpus"]:
         if not hasattr(self.op, attr):
@@ -4796,6 +4826,8 @@ class LUTestAllocator(NoHooksLU):
                                      " 'nics' parameter")
       if not isinstance(self.op.disks, list):
         raise errors.OpPrereqError("Invalid parameter 'disks'")
+      if len(self.op.disks) != 2:
+        raise errors.OpPrereqError("Only two-disk configurations supported")
       for row in self.op.disks:
         if (not isinstance(row, dict) or
             "size" not in row or
@@ -4804,7 +4836,7 @@ class LUTestAllocator(NoHooksLU):
             row["mode"] not in ['r', 'w']):
           raise errors.OpPrereqError("Invalid contents of the"
                                      " 'disks' parameter")
-    elif self.op.mode == constants.ALF_MODE_RELOC:
+    elif self.op.mode == constants.IALLOCATOR_MODE_RELOC:
       if not hasattr(self.op, "name"):
         raise errors.OpPrereqError("Missing attribute 'name' on opcode input")
       fname = self.cfg.ExpandInstanceName(self.op.name)
@@ -4816,11 +4848,10 @@ class LUTestAllocator(NoHooksLU):
       raise errors.OpPrereqError("Invalid test allocator mode '%s'" %
                                  self.op.mode)
 
-    if self.op.direction == constants.ALF_DIR_OUT:
-      if not hasattr(self.op, "allocator"):
+    if self.op.direction == constants.IALLOCATOR_DIR_OUT:
+      if not hasattr(self.op, "allocator") or self.op.allocator is None:
         raise errors.OpPrereqError("Missing allocator name")
-      raise errors.OpPrereqError("Allocator out mode not supported yet")
-    elif self.op.direction != constants.ALF_DIR_IN:
+    elif self.op.direction != constants.IALLOCATOR_DIR_IN:
       raise errors.OpPrereqError("Wrong allocator test '%s'" %
                                  self.op.direction)
 
@@ -4828,14 +4859,18 @@ class LUTestAllocator(NoHooksLU):
     """Run the allocator test.
 
     """
-    data = _AllocatorGetClusterData(self.cfg, self.sstore)
-    if self.op.mode == constants.ALF_MODE_ALLOC:
-      _AllocatorAddNewInstance(data, self.op)
+    data = _IAllocatorGetClusterData(self.cfg, self.sstore)
+    if self.op.mode == constants.IALLOCATOR_MODE_ALLOC:
+      _IAllocatorAddNewInstance(data, self.op)
     else:
-      _AllocatorAddRelocateInstance(data, self.op)
+      _IAllocatorAddRelocateInstance(data, self.op)
 
     if _JSON_INDENT is None:
       text = simplejson.dumps(data)
     else:
       text = simplejson.dumps(data, indent=_JSON_INDENT)
-    return text
+    if self.op.direction == constants.IALLOCATOR_DIR_IN:
+      result = text
+    else:
+      result = _IAllocatorRun(self.op.allocator, text)
+    return result
diff --git a/lib/constants.py b/lib/constants.py
index 7a6e7e709a48d1ab42d1d41ca3b8146d878c47c4..91fcf81aaf155c56ea12eed958c947b555bdb130 100644
--- a/lib/constants.py
+++ b/lib/constants.py
@@ -185,7 +185,8 @@ VERIFY_NPLUSONE_MEM = 'nplusone_mem'
 VERIFY_OPTIONAL_CHECKS = frozenset([VERIFY_NPLUSONE_MEM])
 
 # Allocator framework constants
-ALF_DIR_IN = "in"
-ALF_DIR_OUT = "out"
-ALF_MODE_ALLOC = "allocate"
-ALF_MODE_RELOC = "relocate"
+IALLOCATOR_DIR_IN = "in"
+IALLOCATOR_DIR_OUT = "out"
+IALLOCATOR_MODE_ALLOC = "allocate"
+IALLOCATOR_MODE_RELOC = "relocate"
+IALLOCATOR_SEARCH_PATH = _autoconf.IALLOCATOR_SEARCH_PATH