Merge branch 'stable-2.11' into stable-2.12
[ganeti-github.git] / lib / hypervisor / hv_kvm / __init__.py
index 81f8681..2f22720 100644 (file)
@@ -2,21 +2,30 @@
 #
 
 # Copyright (C) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Google Inc.
+# All rights reserved.
 #
-# 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.
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
 #
-# 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.
+# 1. Redistributions of source code must retain the above copyright notice,
+# this list of conditions and the following disclaimer.
 #
-# 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.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 
 """KVM hypervisor
@@ -31,18 +40,13 @@ import tempfile
 import time
 import logging
 import pwd
-import struct
-import fcntl
 import shutil
 import urllib2
-import socket
-import stat
-import StringIO
 from bitarray import bitarray
 try:
-  import affinity   # pylint: disable=F0401
+  import psutil   # pylint: disable=F0401
 except ImportError:
-  affinity = None
+  psutil = None
 try:
   import fdsend   # pylint: disable=F0401
 except ImportError:
@@ -60,20 +64,14 @@ from ganeti import pathutils
 from ganeti.hypervisor import hv_base
 from ganeti.utils import wrapper as utils_wrapper
 
+from ganeti.hypervisor.hv_kvm.monitor import QmpConnection, QmpMessage, \
+                                             MonitorSocket
+from ganeti.hypervisor.hv_kvm.netdev import OpenTap
+
 
 _KVM_NETWORK_SCRIPT = pathutils.CONF_DIR + "/kvm-vif-bridge"
 _KVM_START_PAUSED_FLAG = "-S"
 
-# TUN/TAP driver constants, taken from <linux/if_tun.h>
-# They are architecture-independent and already hardcoded in qemu-kvm source,
-# so we can safely include them here.
-TUNSETIFF = 0x400454ca
-TUNGETIFF = 0x800454d2
-TUNGETFEATURES = 0x800454cf
-IFF_TAP = 0x0002
-IFF_NO_PI = 0x1000
-IFF_VNET_HDR = 0x4000
-
 #: SPICE parameters which depend on L{constants.HV_KVM_SPICE_BIND}
 _SPICE_ADDITIONAL_PARAMS = frozenset([
   constants.HV_KVM_SPICE_IP_VERSION,
@@ -112,12 +110,33 @@ _RUNTIME_DEVICE = {
   }
 _RUNTIME_ENTRY = {
   constants.HOTPLUG_TARGET_NIC: lambda d, e: d,
-  constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e, None)
+  constants.HOTPLUG_TARGET_DISK: lambda d, e: (d, e[0], e[1])
   }
 
 _MIGRATION_CAPS_DELIM = ":"
 
 
+def _GetDriveURI(disk, link, uri):
+  """Helper function to get the drive uri to be used in --drive kvm option
+
+  @type disk: L{objects.Disk}
+  @param disk: A disk configuration object
+  @type link: string
+  @param link: The device link as returned by _SymlinkBlockDev()
+  @type uri: string
+  @param uri: The drive uri as returned by _CalculateDeviceURI()
+
+  """
+  access_mode = disk.params.get(constants.LDP_ACCESS,
+                                constants.DISK_KERNELSPACE)
+  if (uri and access_mode == constants.DISK_USERSPACE):
+    drive_uri = uri
+  else:
+    drive_uri = link
+
+  return drive_uri
+
+
 def _GenerateDeviceKVMId(dev_type, dev):
   """Helper function to generate a unique device name used by KVM
 
@@ -244,95 +263,6 @@ def _AnalyzeSerializedRuntime(serialized_runtime):
   return (kvm_cmd, kvm_nics, hvparams, kvm_disks)
 
 
-def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
-  """Retrieves supported TUN features from file descriptor.
-
-  @see: L{_ProbeTapVnetHdr}
-
-  """
-  req = struct.pack("I", 0)
-  try:
-    buf = _ioctl(fd, TUNGETFEATURES, req)
-  except EnvironmentError, err:
-    logging.warning("ioctl(TUNGETFEATURES) failed: %s", err)
-    return None
-  else:
-    (flags, ) = struct.unpack("I", buf)
-    return flags
-
-
-def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
-  """Check whether to enable the IFF_VNET_HDR flag.
-
-  To do this, _all_ of the following conditions must be met:
-   1. TUNGETFEATURES ioctl() *must* be implemented
-   2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag
-   3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in
-      drivers/net/tun.c there is no way to test this until after the tap device
-      has been created using TUNSETIFF, and there is no way to change the
-      IFF_VNET_HDR flag after creating the interface, catch-22! However both
-      TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27,
-      thus we can expect TUNGETIFF to be present if TUNGETFEATURES is.
-
-   @type fd: int
-   @param fd: the file descriptor of /dev/net/tun
-
-  """
-  flags = _features_fn(fd)
-
-  if flags is None:
-    # Not supported
-    return False
-
-  result = bool(flags & IFF_VNET_HDR)
-
-  if not result:
-    logging.warning("Kernel does not support IFF_VNET_HDR, not enabling")
-
-  return result
-
-
-def _OpenTap(vnet_hdr=True, name=""):
-  """Open a new tap device and return its file descriptor.
-
-  This is intended to be used by a qemu-type hypervisor together with the -net
-  tap,fd=<fd> command line parameter.
-
-  @type vnet_hdr: boolean
-  @param vnet_hdr: Enable the VNET Header
-
-  @type name: string
-  @param name: name for the TAP interface being created; if an empty
-               string is passed, the OS will generate a unique name
-
-  @return: (ifname, tapfd)
-  @rtype: tuple
-
-  """
-  try:
-    tapfd = os.open("/dev/net/tun", os.O_RDWR)
-  except EnvironmentError:
-    raise errors.HypervisorError("Failed to open /dev/net/tun")
-
-  flags = IFF_TAP | IFF_NO_PI
-
-  if vnet_hdr and _ProbeTapVnetHdr(tapfd):
-    flags |= IFF_VNET_HDR
-
-  # The struct ifreq ioctl request (see netdevice(7))
-  ifr = struct.pack("16sh", name, flags)
-
-  try:
-    res = fcntl.ioctl(tapfd, TUNSETIFF, ifr)
-  except EnvironmentError, err:
-    raise errors.HypervisorError("Failed to allocate a new TAP device: %s" %
-                                 err)
-
-  # Get the interface name from the ioctl
-  ifname = struct.unpack("16sh", res)[0].strip("\x00")
-  return (ifname, tapfd)
-
-
 class HeadRequest(urllib2.Request):
   def get_method(self):
     return "HEAD"
@@ -349,306 +279,6 @@ def _CheckUrl(url):
     return False
 
 
-class QmpMessage:
-  """QEMU Messaging Protocol (QMP) message.
-
-  """
-  def __init__(self, data):
-    """Creates a new QMP message based on the passed data.
-
-    """
-    if not isinstance(data, dict):
-      raise TypeError("QmpMessage must be initialized with a dict")
-
-    self.data = data
-
-  def __getitem__(self, field_name):
-    """Get the value of the required field if present, or None.
-
-    Overrides the [] operator to provide access to the message data,
-    returning None if the required item is not in the message
-    @return: the value of the field_name field, or None if field_name
-             is not contained in the message
-
-    """
-    return self.data.get(field_name, None)
-
-  def __setitem__(self, field_name, field_value):
-    """Set the value of the required field_name to field_value.
-
-    """
-    self.data[field_name] = field_value
-
-  def __len__(self):
-    """Return the number of fields stored in this QmpMessage.
-
-    """
-    return len(self.data)
-
-  def __delitem__(self, key):
-    """Delete the specified element from the QmpMessage.
-
-    """
-    del(self.data[key])
-
-  @staticmethod
-  def BuildFromJsonString(json_string):
-    """Build a QmpMessage from a JSON encoded string.
-
-    @type json_string: str
-    @param json_string: JSON string representing the message
-    @rtype: L{QmpMessage}
-    @return: a L{QmpMessage} built from json_string
-
-    """
-    # Parse the string
-    data = serializer.LoadJson(json_string)
-    return QmpMessage(data)
-
-  def __str__(self):
-    # The protocol expects the JSON object to be sent as a single line.
-    return serializer.DumpJson(self.data)
-
-  def __eq__(self, other):
-    # When comparing two QmpMessages, we are interested in comparing
-    # their internal representation of the message data
-    return self.data == other.data
-
-
-class MonitorSocket(object):
-  _SOCKET_TIMEOUT = 5
-
-  def __init__(self, monitor_filename):
-    """Instantiates the MonitorSocket object.
-
-    @type monitor_filename: string
-    @param monitor_filename: the filename of the UNIX raw socket on which the
-                             monitor (QMP or simple one) is listening
-
-    """
-    self.monitor_filename = monitor_filename
-    self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
-    # We want to fail if the server doesn't send a complete message
-    # in a reasonable amount of time
-    self.sock.settimeout(self._SOCKET_TIMEOUT)
-    self._connected = False
-
-  def _check_socket(self):
-    sock_stat = None
-    try:
-      sock_stat = os.stat(self.monitor_filename)
-    except EnvironmentError, err:
-      if err.errno == errno.ENOENT:
-        raise errors.HypervisorError("No monitor socket found")
-      else:
-        raise errors.HypervisorError("Error checking monitor socket: %s",
-                                     utils.ErrnoOrStr(err))
-    if not stat.S_ISSOCK(sock_stat.st_mode):
-      raise errors.HypervisorError("Monitor socket is not a socket")
-
-  def _check_connection(self):
-    """Make sure that the connection is established.
-
-    """
-    if not self._connected:
-      raise errors.ProgrammerError("To use a MonitorSocket you need to first"
-                                   " invoke connect() on it")
-
-  def connect(self):
-    """Connects to the monitor.
-
-    Connects to the UNIX socket
-
-    @raise errors.HypervisorError: when there are communication errors
-
-    """
-    if self._connected:
-      raise errors.ProgrammerError("Cannot connect twice")
-
-    self._check_socket()
-
-    # Check file existance/stuff
-    try:
-      self.sock.connect(self.monitor_filename)
-    except EnvironmentError:
-      raise errors.HypervisorError("Can't connect to qmp socket")
-    self._connected = True
-
-  def close(self):
-    """Closes the socket
-
-    It cannot be used after this call.
-
-    """
-    self.sock.close()
-
-
-class QmpConnection(MonitorSocket):
-  """Connection to the QEMU Monitor using the QEMU Monitor Protocol (QMP).
-
-  """
-  _FIRST_MESSAGE_KEY = "QMP"
-  _EVENT_KEY = "event"
-  _ERROR_KEY = "error"
-  _RETURN_KEY = RETURN_KEY = "return"
-  _ACTUAL_KEY = ACTUAL_KEY = "actual"
-  _ERROR_CLASS_KEY = "class"
-  _ERROR_DESC_KEY = "desc"
-  _EXECUTE_KEY = "execute"
-  _ARGUMENTS_KEY = "arguments"
-  _CAPABILITIES_COMMAND = "qmp_capabilities"
-  _MESSAGE_END_TOKEN = "\r\n"
-
-  def __init__(self, monitor_filename):
-    super(QmpConnection, self).__init__(monitor_filename)
-    self._buf = ""
-
-  def connect(self):
-    """Connects to the QMP monitor.
-
-    Connects to the UNIX socket and makes sure that we can actually send and
-    receive data to the kvm instance via QMP.
-
-    @raise errors.HypervisorError: when there are communication errors
-    @raise errors.ProgrammerError: when there are data serialization errors
-
-    """
-    super(QmpConnection, self).connect()
-    # Check if we receive a correct greeting message from the server
-    # (As per the QEMU Protocol Specification 0.1 - section 2.2)
-    greeting = self._Recv()
-    if not greeting[self._FIRST_MESSAGE_KEY]:
-      self._connected = False
-      raise errors.HypervisorError("kvm: QMP communication error (wrong"
-                                   " server greeting")
-
-    # This is needed because QMP can return more than one greetings
-    # see https://groups.google.com/d/msg/ganeti-devel/gZYcvHKDooU/SnukC8dgS5AJ
-    self._buf = ""
-
-    # Let's put the monitor in command mode using the qmp_capabilities
-    # command, or else no command will be executable.
-    # (As per the QEMU Protocol Specification 0.1 - section 4)
-    self.Execute(self._CAPABILITIES_COMMAND)
-
-  def _ParseMessage(self, buf):
-    """Extract and parse a QMP message from the given buffer.
-
-    Seeks for a QMP message in the given buf. If found, it parses it and
-    returns it together with the rest of the characters in the buf.
-    If no message is found, returns None and the whole buffer.
-
-    @raise errors.ProgrammerError: when there are data serialization errors
-
-    """
-    message = None
-    # Check if we got the message end token (CRLF, as per the QEMU Protocol
-    # Specification 0.1 - Section 2.1.1)
-    pos = buf.find(self._MESSAGE_END_TOKEN)
-    if pos >= 0:
-      try:
-        message = QmpMessage.BuildFromJsonString(buf[:pos + 1])
-      except Exception, err:
-        raise errors.ProgrammerError("QMP data serialization error: %s" % err)
-      buf = buf[pos + 1:]
-
-    return (message, buf)
-
-  def _Recv(self):
-    """Receives a message from QMP and decodes the received JSON object.
-
-    @rtype: QmpMessage
-    @return: the received message
-    @raise errors.HypervisorError: when there are communication errors
-    @raise errors.ProgrammerError: when there are data serialization errors
-
-    """
-    self._check_connection()
-
-    # Check if there is already a message in the buffer
-    (message, self._buf) = self._ParseMessage(self._buf)
-    if message:
-      return message
-
-    recv_buffer = StringIO.StringIO(self._buf)
-    recv_buffer.seek(len(self._buf))
-    try:
-      while True:
-        data = self.sock.recv(4096)
-        if not data:
-          break
-        recv_buffer.write(data)
-
-        (message, self._buf) = self._ParseMessage(recv_buffer.getvalue())
-        if message:
-          return message
-
-    except socket.timeout, err:
-      raise errors.HypervisorError("Timeout while receiving a QMP message: "
-                                   "%s" % (err))
-    except socket.error, err:
-      raise errors.HypervisorError("Unable to receive data from KVM using the"
-                                   " QMP protocol: %s" % err)
-
-  def _Send(self, message):
-    """Encodes and sends a message to KVM using QMP.
-
-    @type message: QmpMessage
-    @param message: message to send to KVM
-    @raise errors.HypervisorError: when there are communication errors
-    @raise errors.ProgrammerError: when there are data serialization errors
-
-    """
-    self._check_connection()
-    try:
-      message_str = str(message)
-    except Exception, err:
-      raise errors.ProgrammerError("QMP data deserialization error: %s" % err)
-
-    try:
-      self.sock.sendall(message_str)
-    except socket.timeout, err:
-      raise errors.HypervisorError("Timeout while sending a QMP message: "
-                                   "%s (%s)" % (err.string, err.errno))
-    except socket.error, err:
-      raise errors.HypervisorError("Unable to send data from KVM using the"
-                                   " QMP protocol: %s" % err)
-
-  def Execute(self, command, arguments=None):
-    """Executes a QMP command and returns the response of the server.
-
-    @type command: str
-    @param command: the command to execute
-    @type arguments: dict
-    @param arguments: dictionary of arguments to be passed to the command
-    @rtype: dict
-    @return: dictionary representing the received JSON object
-    @raise errors.HypervisorError: when there are communication errors
-    @raise errors.ProgrammerError: when there are data serialization errors
-
-    """
-    self._check_connection()
-    message = QmpMessage({self._EXECUTE_KEY: command})
-    if arguments:
-      message[self._ARGUMENTS_KEY] = arguments
-    self._Send(message)
-
-    # Events can occur between the sending of the command and the reception
-    # of the response, so we need to filter out messages with the event key.
-    while True:
-      response = self._Recv()
-      err = response[self._ERROR_KEY]
-      if err:
-        raise errors.HypervisorError("kvm: error executing the %s"
-                                     " command: %s (%s):" %
-                                     (command,
-                                      err[self._ERROR_DESC_KEY],
-                                      err[self._ERROR_CLASS_KEY]))
-
-      elif not response[self._EVENT_KEY]:
-        return response
-
-
 class KVMHypervisor(hv_base.BaseHypervisor):
   """KVM hypervisor interface
 
@@ -731,12 +361,15 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     constants.HV_USE_LOCALTIME: hv_base.NO_CHECK,
     constants.HV_DISK_CACHE:
       hv_base.ParamInSet(True, constants.HT_VALID_CACHE_TYPES),
+    constants.HV_KVM_DISK_AIO:
+      hv_base.ParamInSet(False, constants.HT_KVM_VALID_AIO_TYPES),
     constants.HV_SECURITY_MODEL:
       hv_base.ParamInSet(True, constants.HT_KVM_VALID_SM_TYPES),
     constants.HV_SECURITY_DOMAIN: hv_base.NO_CHECK,
     constants.HV_KVM_FLAG:
       hv_base.ParamInSet(False, constants.HT_KVM_FLAG_VALUES),
     constants.HV_VHOST_NET: hv_base.NO_CHECK,
+    constants.HV_VIRTIO_NET_QUEUES: hv_base.OPT_VIRTIO_NET_QUEUES_CHECK,
     constants.HV_KVM_USE_CHROOT: hv_base.NO_CHECK,
     constants.HV_KVM_USER_SHUTDOWN: hv_base.NO_CHECK,
     constants.HV_MEM_PATH: hv_base.OPT_DIR_CHECK,
@@ -783,6 +416,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   _QMP_RE = re.compile(r"^-qmp\s", re.M)
   _SPICE_RE = re.compile(r"^-spice\s", re.M)
   _VHOST_RE = re.compile(r"^-net\s.*,vhost=on|off", re.M)
+  _VIRTIO_NET_QUEUES_RE = re.compile(r"^-net\s.*,fds=x:y:...:z", re.M)
   _ENABLE_KVM_RE = re.compile(r"^-enable-kvm\s", re.M)
   _DISABLE_KVM_RE = re.compile(r"^-disable-kvm\s", re.M)
   _NETDEV_RE = re.compile(r"^-netdev\s", re.M)
@@ -951,6 +585,13 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     return utils.PathJoin(cls._CTRL_DIR, "%s.qmp" % instance_name)
 
   @classmethod
+  def _InstanceKvmdMonitor(cls, instance_name):
+    """Returns the instance kvm daemon socket name
+
+    """
+    return utils.PathJoin(cls._CTRL_DIR, "%s.kvmd" % instance_name)
+
+  @classmethod
   def _InstanceShutdownMonitor(cls, instance_name):
     """Returns the instance QMP output filename
 
@@ -1064,6 +705,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   def _ConfigureNIC(instance, seq, nic, tap):
     """Run the network configuration script for a specified NIC
 
+    See L{hv_base.ConfigureNIC}.
+
     @param instance: instance we're acting on
     @type instance: instance object
     @param seq: nic sequence number
@@ -1074,66 +717,26 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     @type tap: str
 
     """
-    env = {
-      "PATH": "%s:/sbin:/usr/sbin" % os.environ["PATH"],
-      "INSTANCE": instance.name,
-      "MAC": nic.mac,
-      "MODE": nic.nicparams[constants.NIC_MODE],
-      "INTERFACE": tap,
-      "INTERFACE_INDEX": str(seq),
-      "INTERFACE_UUID": nic.uuid,
-      "TAGS": " ".join(instance.GetTags()),
-    }
-
-    if nic.ip:
-      env["IP"] = nic.ip
-
-    if nic.name:
-      env["INTERFACE_NAME"] = nic.name
+    hv_base.ConfigureNIC([pathutils.KVM_IFUP, tap], instance, seq, nic, tap)
 
-    if nic.nicparams[constants.NIC_LINK]:
-      env["LINK"] = nic.nicparams[constants.NIC_LINK]
-
-    if nic.nicparams[constants.NIC_VLAN]:
-      env["VLAN"] = nic.nicparams[constants.NIC_VLAN]
-
-    if nic.network:
-      n = objects.Network.FromDict(nic.netinfo)
-      env.update(n.HooksDict())
-
-    if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
-      env["BRIDGE"] = nic.nicparams[constants.NIC_LINK]
+  @classmethod
+  def _SetProcessAffinity(cls, process_id, cpus):
+    """Sets the affinity of a process to the given CPUs.
 
-    result = utils.RunCmd([pathutils.KVM_IFUP, tap], env=env)
-    if result.failed:
-      raise errors.HypervisorError("Failed to configure interface %s: %s;"
-                                   " network configuration script output: %s" %
-                                   (tap, result.fail_reason, result.output))
+    @type process_id: int
+    @type cpus: list of int
+    @param cpus: The list of CPUs the process ID may use.
 
-  @staticmethod
-  def _VerifyAffinityPackage():
-    if affinity is None:
-      raise errors.HypervisorError("affinity Python package not"
+    """
+    if psutil is None:
+      raise errors.HypervisorError("psutil Python package not"
                                    " found; cannot use CPU pinning under KVM")
 
-  @staticmethod
-  def _BuildAffinityCpuMask(cpu_list):
-    """Create a CPU mask suitable for sched_setaffinity from a list of
-    CPUs.
-
-    See man taskset for more info on sched_setaffinity masks.
-    For example: [ 0, 2, 5, 6 ] will return 101 (0x65, 0..01100101).
-
-    @type cpu_list: list of int
-    @param cpu_list: list of physical CPU numbers to map to vCPUs in order
-    @rtype: int
-    @return: a bit mask of CPU affinities
-
-    """
-    if cpu_list == constants.CPU_PINNING_OFF:
-      return constants.CPU_PINNING_ALL_KVM
+    target_process = psutil.Process(process_id)
+    if cpus == constants.CPU_PINNING_OFF:
+      target_process.set_cpu_affinity(range(psutil.cpu_count()))
     else:
-      return sum(2 ** cpu for cpu in cpu_list)
+      target_process.set_cpu_affinity(cpus)
 
   @classmethod
   def _AssignCpuAffinity(cls, cpu_mask, process_id, thread_dict):
@@ -1159,20 +762,16 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       else:
         # If CPU pinning has one non-all entry, map the entire VM to
         # one set of physical CPUs
-        cls._VerifyAffinityPackage()
-        affinity.set_process_affinity_mask(
-          process_id, cls._BuildAffinityCpuMask(all_cpu_mapping))
+        cls._SetProcessAffinity(process_id, all_cpu_mapping)
     else:
       # The number of vCPUs mapped should match the number of vCPUs
       # reported by KVM. This was already verified earlier, so
       # here only as a sanity check.
       assert len(thread_dict) == len(cpu_list)
-      cls._VerifyAffinityPackage()
 
       # For each vCPU, map it to the proper list of physical CPUs
-      for vcpu, i in zip(cpu_list, range(len(cpu_list))):
-        affinity.set_process_affinity_mask(thread_dict[i],
-                                           cls._BuildAffinityCpuMask(vcpu))
+      for i, vcpu in enumerate(cpu_list):
+        cls._SetProcessAffinity(thread_dict[i], vcpu)
 
   def _GetVcpuThreadIds(self, instance_name):
     """Get a mapping of vCPU no. to thread IDs for the instance
@@ -1219,7 +818,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     """
     result = []
     for name in os.listdir(self._PIDS_DIR):
-      if self._InstancePidAlive(name)[2] or self._IsUserShutdown(name):
+      if self._InstancePidAlive(name)[2]:
         result.append(name)
     return result
 
@@ -1237,7 +836,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     @type instance_name: string
     @param instance_name: the instance name
     @type hvparams: dict of strings
-    @param hvparams: hvparams to be used with this instance
+    @param hvparams: hypervisor parameters to be used with this instance
     @rtype: tuple of strings
     @return: (name, id, memory, vcpus, stat, times)
 
@@ -1256,10 +855,10 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     try:
       qmp = QmpConnection(self._InstanceQmpMonitor(instance_name))
       qmp.connect()
-      vcpus = len(qmp.Execute("query-cpus")[qmp.RETURN_KEY])
+      vcpus = len(qmp.Execute("query-cpus"))
       # Will fail if ballooning is not enabled, but we can then just resort to
       # the value above.
-      mem_bytes = qmp.Execute("query-balloon")[qmp.RETURN_KEY][qmp.ACTUAL_KEY]
+      mem_bytes = qmp.Execute("query-balloon")[qmp.ACTUAL_KEY]
       memory = mem_bytes / 1048576
     except errors.HypervisorError:
       pass
@@ -1270,7 +869,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     """Get properties of all instances.
 
     @type hvparams: dict of strings
-    @param hvparams: hypervisor parameter
+    @param hvparams: hypervisor parameters
     @return: list of tuples (name, id, memory, vcpus, stat, times)
 
     """
@@ -1285,12 +884,14 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         data.append(info)
     return data
 
-  def _GenerateKVMBlockDevicesOptions(self, instance, kvm_disks,
+  def _GenerateKVMBlockDevicesOptions(self, instance, up_hvp, kvm_disks,
                                       kvmhelp, devlist):
     """Generate KVM options regarding instance's block devices.
 
     @type instance: L{objects.Instance}
     @param instance: the instance object
+    @type up_hvp: dict
+    @param up_hvp: the instance's runtime hypervisor parameters
     @type kvm_disks: list of tuples
     @param kvm_disks: list of tuples [(disk, link_name, uri)..]
     @type kvmhelp: string
@@ -1301,12 +902,11 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     @return: list of command line options eventually used by kvm executable
 
     """
-    hvp = instance.hvparams
-    kernel_path = hvp[constants.HV_KERNEL_PATH]
+    kernel_path = up_hvp[constants.HV_KERNEL_PATH]
     if kernel_path:
       boot_disk = False
     else:
-      boot_disk = hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
+      boot_disk = up_hvp[constants.HV_BOOT_ORDER] == constants.HT_BO_DISK
 
     # whether this is an older KVM version that uses the boot=on flag
     # on devices
@@ -1314,7 +914,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
 
     dev_opts = []
     device_driver = None
-    disk_type = hvp[constants.HV_DISK_TYPE]
+    disk_type = up_hvp[constants.HV_DISK_TYPE]
     if disk_type == constants.HT_DISK_PARAVIRTUAL:
       if_val = ",if=%s" % self._VIRTIO
       try:
@@ -1326,8 +926,14 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         pass
     else:
       if_val = ",if=%s" % disk_type
+    # AIO mode
+    aio_mode = up_hvp[constants.HV_KVM_DISK_AIO]
+    if aio_mode == constants.HT_KVM_AIO_NATIVE:
+      aio_val = ",aio=%s" % aio_mode
+    else:
+      aio_val = ""
     # Cache mode
-    disk_cache = hvp[constants.HV_DISK_CACHE]
+    disk_cache = up_hvp[constants.HV_DISK_CACHE]
     if instance.disk_template in constants.DTS_EXT_MIRROR:
       if disk_cache != "none":
         # TODO: make this a hard error, instead of a silent overwrite
@@ -1351,15 +957,10 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         if needs_boot_flag and disk_type != constants.HT_DISK_IDE:
           boot_val = ",boot=on"
 
-      access_mode = cfdev.params.get(constants.LDP_ACCESS,
-                                     constants.DISK_KERNELSPACE)
-      if (uri and access_mode == constants.DISK_USERSPACE):
-        drive_uri = uri
-      else:
-        drive_uri = link_name
+      drive_uri = _GetDriveURI(cfdev, link_name, uri)
 
-      drive_val = "file=%s,format=raw%s%s%s" % \
-                  (drive_uri, if_val, boot_val, cache_val)
+      drive_val = "file=%s,format=raw%s%s%s%s" % \
+                  (drive_uri, if_val, boot_val, cache_val, aio_val)
 
       if device_driver:
         # kvm_disks are the 4th entry of runtime file that did not exist in
@@ -1488,6 +1089,10 @@ class KVMHypervisor(hv_base.BaseHypervisor):
         _ = _GetFreeSlot(pci_reservations, reserve=True)
       kvm_cmd.extend(["-soundhw", soundhw])
 
+    if hvp[constants.HV_DISK_TYPE] == constants.HT_DISK_SCSI:
+      # The SCSI controller requires another PCI slot.
+      _ = _GetFreeSlot(pci_reservations, reserve=True)
+
     # Add id to ballon and place to the first available slot (3 or 4)
     addr = _GetFreeSlot(pci_reservations, reserve=True)
     pci_info = ",bus=pci.0,addr=%s" % hex(addr)
@@ -1857,15 +1462,12 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       raise errors.HypervisorError("Failed to start instance %s" % name)
 
   @staticmethod
-  def _GenerateTapName(nic):
+  def _GenerateKvmTapName(nic):
     """Generate a TAP network interface name for a NIC.
 
-    This helper function generates a special TAP network interface
-    name for NICs that are meant to be used in instance communication.
-    This function checks the existing TAP interfaces in order to find
-    a unique name for the new TAP network interface.  The TAP network
-    interface names are of the form 'gnt.com.%d', where '%d' is a
-    unique number within the node.
+    See L{hv_base.GenerateTapName}.
+
+    For the case of the empty string, see L{OpenTap}
 
     @type nic: ganeti.objects.NIC
     @param nic: NIC object for the name should be generated
@@ -1879,30 +1481,56 @@ class KVMHypervisor(hv_base.BaseHypervisor):
           nic.name.startswith(constants.INSTANCE_COMMUNICATION_NIC_PREFIX):
       return ""
 
-    result = utils.RunCmd(["ip", "tuntap", "list"])
-
-    if result.failed:
-      raise errors.HypervisorError("Failed to list TUN/TAP interfaces")
-
-    idxs = set()
+    return hv_base.GenerateTapName()
 
-    for line in result.output.splitlines():
-      parts = line.split(": ", 1)
+  def _GetNetworkDeviceFeatures(self, up_hvp, devlist, kvmhelp):
+    """Get network device options to properly enable supported features.
 
-      if len(parts) < 2:
-        raise errors.HypervisorError("Failed to parse TUN/TAP interfaces")
+    Return tuple of supported and enabled tap features with nic_model.
+    This function is called before opening a new tap device.
 
-      r = re.match(r"gnt\.com\.([0-9]+)", parts[0])
+    @return: (nic_model, vnet_hdr, virtio_net_queues, tap_extra, nic_extra)
+    @rtype: tuple
 
-      if r is not None:
-        idxs.add(int(r.group(1)))
+    """
+    virtio_net_queues = 1
+    nic_extra = ""
+    nic_type = up_hvp[constants.HV_NIC_TYPE]
+    tap_extra = ""
+    vnet_hdr = False
+    if nic_type == constants.HT_NIC_PARAVIRTUAL:
+      nic_model = self._VIRTIO
+      try:
+        if self._VIRTIO_NET_RE.search(devlist):
+          nic_model = self._VIRTIO_NET_PCI
+          vnet_hdr = up_hvp[constants.HV_VNET_HDR]
+      except errors.HypervisorError, _:
+        # Older versions of kvm don't support DEVICE_LIST, but they don't
+        # have new virtio syntax either.
+        pass
 
-    if idxs:
-      idx = max(idxs) + 1
+      if up_hvp[constants.HV_VHOST_NET]:
+        # Check for vhost_net support.
+        if self._VHOST_RE.search(kvmhelp):
+          tap_extra = ",vhost=on"
+        else:
+          raise errors.HypervisorError("vhost_net is configured"
+                                       " but it is not available")
+        if up_hvp[constants.HV_VIRTIO_NET_QUEUES] > 1:
+          # Check for multiqueue virtio-net support.
+          if self._VIRTIO_NET_QUEUES_RE.search(kvmhelp):
+            virtio_net_queues = up_hvp[constants.HV_VIRTIO_NET_QUEUES]
+            # As advised at http://www.linux-kvm.org/page/Multiqueue formula
+            # for calculating vector size is: vectors=2*N+1 where N is the
+            # number of queues (HV_VIRTIO_NET_QUEUES).
+            nic_extra = ",mq=on,vectors=%d" % (2 * virtio_net_queues + 1)
+          else:
+            raise errors.HypervisorError("virtio_net_queues is configured"
+                                         " but it is not available")
     else:
-      idx = 0
+      nic_model = nic_type
 
-    return "gnt.com.%d" % idx
+    return (nic_model, vnet_hdr, virtio_net_queues, tap_extra, nic_extra)
 
   # too many local variables
   # pylint: disable=R0914
@@ -1962,37 +1590,18 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     if not kvm_nics:
       kvm_cmd.extend(["-net", "none"])
     else:
-      vnet_hdr = False
-      tap_extra = ""
-      nic_type = up_hvp[constants.HV_NIC_TYPE]
-      if nic_type == constants.HT_NIC_PARAVIRTUAL:
-        nic_model = self._VIRTIO
-        try:
-          if self._VIRTIO_NET_RE.search(devlist):
-            nic_model = self._VIRTIO_NET_PCI
-            vnet_hdr = up_hvp[constants.HV_VNET_HDR]
-        except errors.HypervisorError, _:
-          # Older versions of kvm don't support DEVICE_LIST, but they don't
-          # have new virtio syntax either.
-          pass
-
-        if up_hvp[constants.HV_VHOST_NET]:
-          # check for vhost_net support
-          if self._VHOST_RE.search(kvmhelp):
-            tap_extra = ",vhost=on"
-          else:
-            raise errors.HypervisorError("vhost_net is configured"
-                                         " but it is not available")
-      else:
-        nic_model = nic_type
-
+      (nic_model, vnet_hdr,
+       virtio_net_queues, tap_extra,
+       nic_extra) = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
       kvm_supports_netdev = self._NETDEV_RE.search(kvmhelp)
-
       for nic_seq, nic in enumerate(kvm_nics):
-        tapname, tapfd = _OpenTap(vnet_hdr=vnet_hdr,
-                                  name=self._GenerateTapName(nic))
-        tapfds.append(tapfd)
+        tapname, nic_tapfds = OpenTap(vnet_hdr=vnet_hdr,
+                                      virtio_net_queues=virtio_net_queues,
+                                      name=self._GenerateKvmTapName(nic))
+        tapfds.extend(nic_tapfds)
         taps.append(tapname)
+        tapfd = "%s%s" % ("fds=" if len(nic_tapfds) > 1 else "fd=",
+                          ":".join(str(fd) for fd in nic_tapfds))
         if kvm_supports_netdev:
           nic_val = "%s,mac=%s" % (nic_model, nic.mac)
           try:
@@ -2003,14 +1612,14 @@ class KVMHypervisor(hv_base.BaseHypervisor):
             nic_val += (",id=%s,bus=pci.0,addr=%s" % (kvm_devid, hex(nic.pci)))
           except errors.HotplugError:
             netdev = "netdev%d" % nic_seq
-          nic_val += (",netdev=%s" % netdev)
-          tap_val = ("type=tap,id=%s,fd=%d%s" %
+          nic_val += (",netdev=%s%s" % (netdev, nic_extra))
+          tap_val = ("type=tap,id=%s,%s%s" %
                      (netdev, tapfd, tap_extra))
           kvm_cmd.extend(["-netdev", tap_val, "-device", nic_val])
         else:
           nic_val = "nic,vlan=%s,macaddr=%s,model=%s" % (nic_seq,
                                                          nic.mac, nic_model)
-          tap_val = "tap,vlan=%s,fd=%d" % (nic_seq, tapfd)
+          tap_val = "tap,vlan=%s,%s" % (nic_seq, tapfd)
           kvm_cmd.extend(["-net", tap_val, "-net", nic_val])
 
     if incoming:
@@ -2038,16 +1647,21 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       logging.debug("Enabling QMP")
       kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
                       self._InstanceQmpMonitor(instance.name)])
+      # Add a second monitor for kvmd
+      kvm_cmd.extend(["-qmp", "unix:%s,server,nowait" %
+                      self._InstanceKvmdMonitor(instance.name)])
 
-    # Configure the network now for starting instances and bridged interfaces,
-    # during FinalizeMigration for incoming instances' routed interfaces
+    # Configure the network now for starting instances and bridged/OVS
+    # interfaces, during FinalizeMigration for incoming instances' routed
+    # interfaces.
     for nic_seq, nic in enumerate(kvm_nics):
       if (incoming and
-          nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_BRIDGED):
+          nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_ROUTED):
         continue
       self._ConfigureNIC(instance, nic_seq, nic, taps[nic_seq])
 
     bdev_opts = self._GenerateKVMBlockDevicesOptions(instance,
+                                                     up_hvp,
                                                      kvm_disks,
                                                      kvmhelp,
                                                      devlist)
@@ -2137,13 +1751,16 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   def _StartKvmd(hvparams):
     """Ensure that the Kvm daemon is running.
 
+    @type hvparams: dict of strings
+    @param hvparams: hypervisor parameters
+
     """
     if hvparams is None \
           or not hvparams[constants.HV_KVM_USER_SHUTDOWN] \
           or utils.IsDaemonAlive(constants.KVMD):
       return
 
-    result = utils.RunCmd(constants.KVMD)
+    result = utils.RunCmd([pathutils.DAEMON_UTIL, "start", constants.KVMD])
 
     if result.failed:
       raise errors.HypervisorError("Failed to start KVM daemon")
@@ -2220,12 +1837,13 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       hvp = instance.hvparams
       security_model = hvp[constants.HV_SECURITY_MODEL]
       use_chroot = hvp[constants.HV_KVM_USE_CHROOT]
-      if use_chroot:
-        raise errors.HotplugError("Disk hotplug is not supported"
-                                  " in case of chroot.")
-      if security_model != constants.HT_SM_NONE:
-        raise errors.HotplugError("Disk Hotplug is not supported in case"
-                                  " security models are used.")
+      if action == constants.HOTPLUG_ACTION_ADD:
+        if use_chroot:
+          raise errors.HotplugError("Disk hotplug is not supported"
+                                    " in case of chroot.")
+        if security_model != constants.HT_SM_NONE:
+          raise errors.HotplugError("Disk Hotplug is not supported in case"
+                                    " security models are used.")
 
     if (dev_type == constants.HOTPLUG_TARGET_NIC and
         action == constants.HOTPLUG_ACTION_ADD and not fdsend):
@@ -2298,17 +1916,29 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     kvm_devid = _GenerateDeviceKVMId(dev_type, device)
     runtime = self._LoadKVMRuntime(instance)
     if dev_type == constants.HOTPLUG_TARGET_DISK:
+      drive_uri = _GetDriveURI(device, extra[0], extra[1])
       cmds = ["drive_add dummy file=%s,if=none,id=%s,format=raw" %
-                (extra, kvm_devid)]
+                (drive_uri, kvm_devid)]
       cmds += ["device_add virtio-blk-pci,bus=pci.0,addr=%s,drive=%s,id=%s" %
                 (hex(device.pci), kvm_devid, kvm_devid)]
     elif dev_type == constants.HOTPLUG_TARGET_NIC:
-      (tap, fd) = _OpenTap()
+      kvmpath = instance.hvparams[constants.HV_KVM_PATH]
+      kvmhelp = self._GetKVMOutput(kvmpath, self._KVMOPT_HELP)
+      devlist = self._GetKVMOutput(kvmpath, self._KVMOPT_DEVICELIST)
+      up_hvp = runtime[2]
+      (_, vnet_hdr,
+       virtio_net_queues, tap_extra,
+       nic_extra) = self._GetNetworkDeviceFeatures(up_hvp, devlist, kvmhelp)
+      (tap, fds) = OpenTap(vnet_hdr=vnet_hdr,
+                           virtio_net_queues=virtio_net_queues)
+      # netdev_add don't support "fds=" when multiple fds are
+      # requested, generate separate "fd=" string for every fd
+      tapfd = ",".join(["fd=%s" % fd for fd in fds])
       self._ConfigureNIC(instance, seq, device, tap)
-      self._PassTapFd(instance, fd, device)
-      cmds = ["netdev_add tap,id=%s,fd=%s" % (kvm_devid, kvm_devid)]
-      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s" % \
-               (hex(device.pci), device.mac, kvm_devid, kvm_devid)
+      self._PassTapFd(instance, fds, device)
+      cmds = ["netdev_add tap,id=%s,%s%s" % (kvm_devid, tapfd, tap_extra)]
+      args = "virtio-net-pci,bus=pci.0,addr=%s,mac=%s,netdev=%s,id=%s%s" % \
+               (hex(device.pci), device.mac, kvm_devid, kvm_devid, nic_extra)
       cmds += ["device_add %s" % args]
       utils.WriteFile(self._InstanceNICFile(instance.name, seq), data=tap)
 
@@ -2358,7 +1988,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       device.pci = self.HotDelDevice(instance, dev_type, device, _, seq)
       self.HotAddDevice(instance, dev_type, device, _, seq)
 
-  def _PassTapFd(self, instance, fd, nic):
+  def _PassTapFd(self, instance, fds, nic):
     """Pass file descriptor to kvm process via monitor socket using SCM_RIGHTS
 
     """
@@ -2366,7 +1996,6 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     #       squash common parts between monitor and qmp
     kvm_devid = _GenerateDeviceKVMId(constants.HOTPLUG_TARGET_NIC, nic)
     command = "getfd %s\n" % kvm_devid
-    fds = [fd]
     logging.info("%s", fds)
     try:
       monsock = MonitorSocket(self._InstanceMonitor(instance.name))
@@ -2547,8 +2176,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
       kvm_nics = kvm_runtime[1]
 
       for nic_seq, nic in enumerate(kvm_nics):
-        if nic.nicparams[constants.NIC_MODE] == constants.NIC_MODE_BRIDGED:
-          # Bridged interfaces have already been configured
+        if nic.nicparams[constants.NIC_MODE] != constants.NIC_MODE_ROUTED:
+          # Bridged/OVS interfaces have already been configured
           continue
         try:
           tap = utils.ReadFile(self._InstanceNICFile(instance.name, nic_seq))
@@ -2763,8 +2392,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   def CheckParameterSyntax(cls, hvparams):
     """Check the given parameters for validity.
 
-    @type hvparams:  dict
-    @param hvparams: dictionary with parameter names/value
+    @type hvparams: dict of strings
+    @param hvparams: hypervisor parameters
     @raise errors.HypervisorError: when a parameter is not valid
 
     """
@@ -2836,8 +2465,8 @@ class KVMHypervisor(hv_base.BaseHypervisor):
   def ValidateParameters(cls, hvparams):
     """Check the given parameters for validity.
 
-    @type hvparams:  dict
-    @param hvparams: dictionary with parameter names/value
+    @type hvparams: dict of strings
+    @param hvparams: hypervisor parameters
     @raise errors.HypervisorError: when a parameter is not valid
 
     """
@@ -2898,7 +2527,7 @@ class KVMHypervisor(hv_base.BaseHypervisor):
     """KVM powercycle, just a wrapper over Linux powercycle.
 
     @type hvparams: dict of strings
-    @param hvparams: hypervisor params to be used on this node
+    @param hvparams: hypervisor parameters to be used on this node
 
     """
     cls.LinuxPowercycle()