This method will create and open a macvtap device using the `ip-link`
command.

It takes as arguments the name of the device to be created, its mac
address, the macvtap mode, the link where the macvtap device will be
attached to, and a dictionary of features to be enabled for the device.
Currently, these features are vhost, and multiqueue. The vnet_hdr is
enabled by default and there is no way to disable it, after creating the
device.

Also, we've created a helper method to get the fds of the /dev/vhost-net
device, since that part of code will be used by both the OpenMacVTap and
OpenTap methods.

Signed-off-by: Dimitris Bliablias <db...@skroutz.gr>
---
 lib/hypervisor/hv_kvm/netdev.py | 99 +++++++++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/lib/hypervisor/hv_kvm/netdev.py b/lib/hypervisor/hv_kvm/netdev.py
index 852d48f..c65aef8 100644
--- a/lib/hypervisor/hv_kvm/netdev.py
+++ b/lib/hypervisor/hv_kvm/netdev.py
@@ -37,7 +37,9 @@ import logging
 import struct
 import fcntl
 
+from ganeti import utils
 from ganeti import errors
+from ganeti import constants
 
 
 # TUN/TAP driver constants, taken from <linux/if_tun.h>
@@ -124,6 +126,21 @@ def _ProbeTapMqVirtioNet(fd, _features_fn=_GetTunFeatures):
   return result
 
 
+def _GetVhostFd():
+  """Return a /dev/vhost-net file descriptor.
+
+  This is done regularly by the qemu process if vhost=on was passed with
+  --netdev option. Still, in case of hotplug and if the process does not
+  run with root privileges, we have to get the fds and pass them via
+  SCM_RIGHTS prior to qemu using them.
+
+  """
+  try:
+    return os.open("/dev/vhost-net", os.O_RDWR)
+  except EnvironmentError:
+    raise errors.HypervisorError("Failed to open /dev/vhost-net")
+
+
 def OpenTap(name="", features=None):
   """Open a new tap device and return its file descriptor.
 
@@ -177,15 +194,7 @@ def OpenTap(name="", features=None):
                                    err)
 
     if vhost:
-      # This is done regularly by the qemu process if vhost=on was passed with
-      # --netdev option. Still, in case of hotplug and if the process does not
-      # run with root privileges, we have to get the fds and pass them via
-      # SCM_RIGHTS prior to qemu using them.
-      try:
-        vhostfd = os.open("/dev/vhost-net", os.O_RDWR)
-        vhostfds.append(vhostfd)
-      except EnvironmentError:
-        raise errors.HypervisorError("Failed to open /dev/vhost-net")
+      vhostfds.append(_GetVhostFd())
 
     tapfds.append(tapfd)
 
@@ -193,3 +202,75 @@ def OpenTap(name="", features=None):
   ifname = struct.unpack("16sh", res)[0].strip("\x00")
 
   return (ifname, tapfds, vhostfds)
+
+
+def OpenMacVTap(name, link, mac, macvtap_mode, features=None):
+  """Open a new macvtap device and return its file descriptor.
+
+  This is intended to be used by a qemu-type hypervisor together with the
+  -netdev type=tap,fd=<fd>,id=<id> -device virtio-net-pci,netdev=<id>,mac=<mac>
+  command line parameter.
+
+  @type name: string
+  @param name: name for the macvtap interface to be created; name created
+      using the L{hv_base.GenerateTapName} helper method
+  @type link: string
+  @param link: specifies the physical device to operate on
+  @type mac: string
+  @param mac: specifies the MAC address of the macvtap interface
+  @type macvtap_mode: string
+  @param macvtap_mode: specifies the type of the new device; vepa, bridge,
+    private, or passthru
+  @type features: dict
+  @param features: A dict denoting whether vhost or mq netdev features
+    are enabled or not
+
+  @rtype: tuple
+  @return: (ifname, [tapfd], [vhostfd])
+
+  """
+  ifname = name
+  tapfd = []
+  vhostfd = []
+
+  if features is None:
+    features = {}
+  vhost = features.get("vhost", False)
+  _, virtio_net_queues = features.get("mq", (False, 1))
+
+  if not ifname:
+    raise errors.HypervisorError("Cannot create macvtap device: device name"
+                                 " is empty")
+
+  # Construct the ip-link command for the macvtap interface
+  cmd = ["ip", "link", "add"]
+  cmd.extend(["link", link])
+  cmd.extend(["name", name])
+  cmd.extend(["address", mac])
+  if virtio_net_queues > 1:
+    cmd.extend(["numtxqueues", virtio_net_queues])
+    cmd.extend(["numrxqueues", virtio_net_queues])
+  cmd.extend(["type", constants.NIC_MODE_MACVTAP])
+  cmd.extend(["mode", macvtap_mode])
+
+  result = utils.RunCmd(cmd)
+  if result.failed:
+    raise errors.HypervisorError("Failed to create macvtap device: %s (%s)" %
+                                 (ifname, result.fail_reason))
+
+  try:
+    with open(utils.PathJoin("/sys/class/net", name, "ifindex"), "r") as fd:
+      ifindex = fd.read().strip()
+  except IOError, err:
+    raise errors.HypervisorError("Failed to read from sysfs %s: %s" %
+                                 (name, err))
+
+  try:
+    tapfd.append(os.open("/dev/tap%s" % ifindex, os.O_RDWR))
+  except EnvironmentError:
+    raise errors.HypervisorError("Failed to open /dev/tap%s" % ifindex)
+
+  if vhost:
+    vhostfd = [_GetVhostFd()]
+
+  return (ifname, tapfd, vhostfd)
-- 
2.1.4

Reply via email to