diff --git a/Makefile.am b/Makefile.am
index ce7acacf2297c75cdf9ffebdb66610f8acbfb090..6c4b2c62c30056b16c434b0e4a6c49a2c3cc5f7a 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -89,6 +89,7 @@ hypervisor_PYTHON = \
 	lib/hypervisor/__init__.py \
 	lib/hypervisor/hv_base.py \
 	lib/hypervisor/hv_fake.py \
+	lib/hypervisor/hv_kvm.py \
 	lib/hypervisor/hv_xen.py
 
 rapi_PYTHON = \
diff --git a/lib/hypervisor/__init__.py b/lib/hypervisor/__init__.py
index c3e5ece1f46e7d50a646d98207ccad8834e6f3dd..035ce86957c79dacbf47975419105c990bdf4030 100644
--- a/lib/hypervisor/__init__.py
+++ b/lib/hypervisor/__init__.py
@@ -29,12 +29,14 @@ from ganeti import errors
 
 from ganeti.hypervisor import hv_fake
 from ganeti.hypervisor import hv_xen
+from ganeti.hypervisor import hv_kvm
 
 
 _HYPERVISOR_MAP = {
     constants.HT_XEN_PVM30: hv_xen.XenPvmHypervisor,
     constants.HT_XEN_HVM31: hv_xen.XenHvmHypervisor,
     constants.HT_FAKE: hv_fake.FakeHypervisor,
+    constants.HT_KVM: hv_kvm.KVMHypervisor,
     }
 
 
diff --git a/lib/hypervisor/hv_kvm.py b/lib/hypervisor/hv_kvm.py
new file mode 100644
index 0000000000000000000000000000000000000000..66d63abb1eac1fa8035e4c736be09e3c9c3a6789
--- /dev/null
+++ b/lib/hypervisor/hv_kvm.py
@@ -0,0 +1,382 @@
+#
+#
+
+# 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.
+
+
+"""KVM hypervisor
+
+"""
+
+import os
+import os.path
+import re
+import tempfile
+from cStringIO import StringIO
+
+from ganeti import utils
+from ganeti import constants
+from ganeti import errors
+from ganeti.hypervisor import hv_base
+
+
+class KVMHypervisor(hv_base.BaseHypervisor):
+  """Fake hypervisor interface.
+
+  This can be used for testing the ganeti code without having to have
+  a real virtualisation software installed.
+
+  """
+  _ROOT_DIR = constants.RUN_GANETI_DIR + "/kvm-hypervisor"
+  _PIDS_DIR = _ROOT_DIR + "/pid"
+  _CTRL_DIR = _ROOT_DIR + "/ctrl"
+  _DIRS = [_ROOT_DIR, _PIDS_DIR, _CTRL_DIR]
+
+  def __init__(self):
+    hv_base.BaseHypervisor.__init__(self)
+    # Let's make sure the directories we need exist, even if the RUN_DIR lives
+    # in a tmpfs filesystem or has been otherwise wiped out.
+    for dir in self._DIRS:
+      if not os.path.exists(dir):
+        os.mkdir(dir)
+
+  def _WriteNetScript(self, instance, seq, nic):
+    """Write a script to connect a net interface to the proper bridge.
+
+    This can be used by any qemu-type hypervisor.
+
+    @param instance: instance we're acting on
+    @type instance: instance object
+    @param seq: nic sequence number
+    @type seq: int
+    @param nic: nic we're acting on
+    @type nic: nic object
+    @return: netscript file name
+    @rtype: string
+
+    """
+    script = StringIO()
+    script.write("#!/bin/sh\n")
+    script.write("# this is autogenerated by Ganeti, please do not edit\n#\n")
+    script.write("export INSTANCE=%s\n" % instance.name)
+    script.write("export MAC=%s\n" % nic.mac)
+    script.write("export IP=%s\n" % nic.ip)
+    script.write("export BRIDGE=%s\n" % nic.bridge)
+    script.write("export INTERFACE=$1\n")
+    # TODO: make this configurable at ./configure time
+    script.write("if [ -x /etc/ganeti/kvm-vif-bridge ]; then\n")
+    script.write("  # Execute the user-specific vif file\n")
+    script.write("  /etc/ganeti/kvm-vif-bridge\n")
+    script.write("else\n")
+    script.write("  # Connect the interface to the bridge\n")
+    script.write("  /sbin/ifconfig $INTERFACE 0.0.0.0 up\n")
+    script.write("  /usr/sbin/brctl addif $BRIDGE $INTERFACE\n")
+    script.write("fi\n\n")
+    # As much as we'd like to put this in our _ROOT_DIR, that will happen to be
+    # mounted noexec sometimes, so we'll have to find another place.
+    (tmpfd, tmpfile_name) = tempfile.mkstemp()
+    tmpfile = os.fdopen(tmpfd, 'w')
+    tmpfile.write(script.getvalue())
+    tmpfile.close()
+    os.chmod(tmpfile_name, 0755)
+    return tmpfile_name
+
+  def ListInstances(self):
+    """Get the list of running instances.
+
+    We can do this by listing our live instances directory and checking whether
+    the associated kvm process is still alive.
+
+    """
+    result = []
+    for name in os.listdir(self._PIDS_DIR):
+      file = "%s/%s" % (self._PIDS_DIR, name)
+      if utils.IsProcessAlive(utils.ReadPidFile(file)):
+        result.append(name)
+    return result
+
+  def GetInstanceInfo(self, instance_name):
+    """Get instance properties.
+
+    Args:
+      instance_name: the instance name
+
+    Returns:
+      (name, id, memory, vcpus, stat, times)
+    """
+    pidfile = "%s/%s" % (self._PIDS_DIR, instance_name)
+    pid = utils.ReadPidFile(pidfile)
+    if not utils.IsProcessAlive(pid):
+      return None
+
+    cmdline_file = "/proc/%s/cmdline" % pid
+    try:
+      fh = open(cmdline_file, 'r')
+      try:
+        cmdline = fh.read()
+      finally:
+        fh.close()
+    except IOError, err:
+      raise errors.HypervisorError("Failed to list instance %s: %s" %
+                                   (instance_name, err))
+
+    memory = 0
+    vcpus = 0
+    stat = "---b-"
+    times = "0"
+
+    arg_list = cmdline.split('\x00')
+    while arg_list:
+      arg =  arg_list.pop(0)
+      if arg == '-m':
+        memory = arg_list.pop(0)
+      elif arg == '-smp':
+        vcpus = arg_list.pop(0)
+
+    return (instance_name, pid, memory, vcpus, stat, times)
+
+
+  def GetAllInstancesInfo(self):
+    """Get properties of all instances.
+
+    Returns:
+      [(name, id, memory, vcpus, stat, times),...]
+    """
+    data = []
+    for name in os.listdir(self._PIDS_DIR):
+      file = "%s/%s" % (self._PIDS_DIR, name)
+      if utils.IsProcessAlive(utils.ReadPidFile(file)):
+        data.append(self.GetInstanceInfo(name))
+
+    return data
+
+  def StartInstance(self, instance, block_devices, extra_args):
+    """Start an instance.
+
+    """
+    temp_files = []
+    pidfile = self._PIDS_DIR + "/%s" % instance.name
+    if utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
+      raise errors.HypervisorError("Failed to start instance %s: %s" %
+                                   (instance.name, "already running"))
+
+    kvm = constants.KVM_PATH
+    kvm_cmd = [kvm]
+    kvm_cmd.extend(['-m', instance.memory])
+    kvm_cmd.extend(['-smp', instance.vcpus])
+    kvm_cmd.extend(['-pidfile', pidfile])
+    # used just by the vnc server, if enabled
+    kvm_cmd.extend(['-name', instance.name])
+    kvm_cmd.extend(['-daemonize'])
+    if not instance.hvm_acpi:
+      kvm_cmd.extend(['-no-acpi'])
+    if not instance.nics:
+      kvm_cmd.extend(['-net', 'none'])
+    else:
+      nic_seq = 0
+      for nic in instance.nics:
+        script = self._WriteNetScript(instance, nic_seq, nic)
+        # FIXME: handle other models
+        nic_val = "nic,macaddr=%s,model=virtio" % nic.mac
+        kvm_cmd.extend(['-net', nic_val])
+        # if os.path.blah(script):
+        kvm_cmd.extend(['-net', 'tap,script=%s' % script])
+        temp_files.append(script)
+        nic_seq += 1
+
+    boot_drive = True
+    for cfdev, rldev in block_devices:
+      # TODO: handle FD_LOOP and FD_BLKTAP (?)
+      # TODO: handle if= correctly
+      #drive_val = 'file=%s,format=raw,if=virtio' % rldev.dev_path
+      if boot_drive:
+        boot_val = ',boot=on'
+      else:
+        boot_val = ''
+
+      drive_val = 'file=%s,format=raw,if=virtio' % rldev.dev_path
+      if boot_drive:
+        drive_val = '%s,boot=on' % drive_val
+        boot_drive = False
+      #drive_val = 'file=%s,if=virtio' % rldev.dev_path
+      kvm_cmd.extend(['-drive', drive_val])
+      #flagname = cfdev.iv_name.replace('s', 'h', 1)
+      #kvm_cmd.extend(['-%s' % flagname, drive_val])
+
+    # kernel handling
+    if instance.kernel_path in (None, constants.VALUE_DEFAULT):
+      kpath = constants.XEN_KERNEL # FIXME: other name??
+    else:
+      if not os.path.exists(instance.kernel_path):
+        raise errors.HypervisorError("The kernel %s for instance %s is"
+                                     " missing" % (instance.kernel_path,
+                                                   instance.name))
+      kpath = instance.kernel_path
+
+    kvm_cmd.extend(['-kernel', kpath])
+
+    # initrd handling
+    if instance.initrd_path in (None, constants.VALUE_DEFAULT):
+      if os.path.exists(constants.XEN_INITRD):
+        initrd_path = constants.XEN_INITRD
+      else:
+        initrd_path = None
+    elif instance.initrd_path == constants.VALUE_NONE:
+      initrd_path = None
+    else:
+      if not os.path.exists(instance.initrd_path):
+        raise errors.HypervisorError("The initrd %s for instance %s is"
+                                     " missing" % (instance.initrd_path,
+                                                   instance.name))
+      initrd_path = instance.initrd_path
+
+    if initrd_path:
+      kvm_cmd.extend(['-initrd', initrd_path])
+
+    kvm_cmd.extend(['-append', 'console=ttyS0,38400 root=/dev/vda'])
+
+    #"hvm_boot_order",
+    #"hvm_pae",
+    #"hvm_cdrom_image_path",
+
+    kvm_cmd.extend(['-nographic'])
+    # FIXME: handle vnc, if needed
+    # How do we decide whether to have it or not?? :(
+    #"vnc_bind_address",
+    #"network_port"
+    base_control = '%s/%s' % (self._CTRL_DIR, instance.name)
+    monitor_dev = 'unix:%s.monitor,server,nowait' % base_control
+    kvm_cmd.extend(['-monitor', monitor_dev])
+    serial_dev = 'unix:%s.serial,server,nowait' % base_control
+    kvm_cmd.extend(['-serial', serial_dev])
+
+    result = utils.RunCmd(kvm_cmd)
+    if result.failed:
+      raise errors.HypervisorError("Failed to start instance %s: %s (%s)" %
+                                   (instance.name, result.fail_reason,
+                                    result.output))
+
+    if not utils.IsProcessAlive(utils.ReadPidFile(pidfile)):
+      raise errors.HypervisorError("Failed to start instance %s: %s" %
+                                   (instance.name))
+
+    for file in temp_files:
+      utils.RemoveFile(file)
+
+  def StopInstance(self, instance, force=False):
+    """Stop an instance.
+
+    """
+    pid_file = self._PIDS_DIR + "/%s" % instance.name
+    pid = utils.ReadPidFile(pid_file)
+    if pid > 0 and utils.IsProcessAlive(pid):
+      if force or not instance.hvm_acpi:
+        utils.KillProcess(pid)
+      else:
+        # This only works if the instance os has acpi support
+        monitor_socket = '%s/%s.monitor'  % (self._CTRL_DIR, instance.name)
+        socat = 'socat -u STDIN UNIX-CONNECT:%s' % monitor_socket
+        command = "echo 'system_powerdown' | %s" % socat
+        result = utils.RunCmd(command)
+        if result.failed:
+          raise errors.HypervisorError("Failed to stop instance %s: %s" %
+                                       (instance.name, result.fail_reason))
+
+    if not utils.IsProcessAlive(pid):
+      utils.RemoveFile(pid_file)
+
+  def RebootInstance(self, instance):
+    """Reboot an instance.
+
+    """
+    # For some reason if we do a 'send-key ctrl-alt-delete' to the control
+    # socket the instance will stop, but now power up again. So we'll resort
+    # to shutdown and restart.
+    self.StopInstance(instance)
+    self.StartInstance(instance)
+
+  def GetNodeInfo(self):
+    """Return information about the node.
+
+    The return value is a dict, which has to have the following items:
+      (all values in MiB)
+      - memory_total: the total memory size on the node
+      - memory_free: the available memory on the node for instances
+      - memory_dom0: the memory used by the node itself, if available
+
+    """
+    # global ram usage from the xm info command
+    # memory                 : 3583
+    # free_memory            : 747
+    # note: in xen 3, memory has changed to total_memory
+    try:
+      fh = file("/proc/meminfo")
+      try:
+        data = fh.readlines()
+      finally:
+        fh.close()
+    except IOError, err:
+      raise errors.HypervisorError("Failed to list node info: %s" % err)
+
+    result = {}
+    sum_free = 0
+    for line in data:
+      splitfields = line.split(":", 1)
+
+      if len(splitfields) > 1:
+        key = splitfields[0].strip()
+        val = splitfields[1].strip()
+        if key == 'MemTotal':
+          result['memory_total'] = int(val.split()[0])/1024
+        elif key in ('MemFree', 'Buffers', 'Cached'):
+          sum_free += int(val.split()[0])/1024
+        elif key == 'Active':
+          result['memory_dom0'] = int(val.split()[0])/1024
+    result['memory_free'] = sum_free
+
+    cpu_total = 0
+    try:
+      fh = open("/proc/cpuinfo")
+      try:
+        cpu_total = len(re.findall("(?m)^processor\s*:\s*[0-9]+\s*$",
+                                   fh.read()))
+      finally:
+        fh.close()
+    except EnvironmentError, err:
+      raise errors.HypervisorError("Failed to list node info: %s" % err)
+    result['cpu_total'] = cpu_total
+
+    return result
+
+  @staticmethod
+  def GetShellCommandForConsole(instance):
+    """Return a command for connecting to the console of an instance.
+
+    """
+    # TODO: we can either try the serial socket or suggest vnc
+    return "echo Console not available for the kvm hypervisor yet"
+
+  def Verify(self):
+    """Verify the hypervisor.
+
+    Check that the binary exists.
+
+    """
+    if not os.path.exists(constants.KVM_PATH):
+      return "The kvm binary ('%s') does not exist." % constants.KVM_PATH
+