Package ganeti :: Package hypervisor :: Package hv_kvm :: Module netdev
[hide private]
[frames] | no frames]

Source Code for Module ganeti.hypervisor.hv_kvm.netdev

  1  # 
  2  # 
  3   
  4  # Copyright (C) 2014 Google Inc. 
  5  # All rights reserved. 
  6  # 
  7  # Redistribution and use in source and binary forms, with or without 
  8  # modification, are permitted provided that the following conditions are 
  9  # met: 
 10  # 
 11  # 1. Redistributions of source code must retain the above copyright notice, 
 12  # this list of conditions and the following disclaimer. 
 13  # 
 14  # 2. Redistributions in binary form must reproduce the above copyright 
 15  # notice, this list of conditions and the following disclaimer in the 
 16  # documentation and/or other materials provided with the distribution. 
 17  # 
 18  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 
 19  # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 
 20  # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 
 21  # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR 
 22  # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
 23  # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
 24  # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
 25  # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
 26  # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
 27  # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
 28  # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 29   
 30   
 31  """KVM hypervisor tap device helpers 
 32   
 33  """ 
 34   
 35  import os 
 36  import logging 
 37  import struct 
 38  import fcntl 
 39   
 40  from ganeti import errors 
 41   
 42   
 43  # TUN/TAP driver constants, taken from <linux/if_tun.h> 
 44  # They are architecture-independent and already hardcoded in qemu-kvm source, 
 45  # so we can safely include them here. 
 46  TUNSETIFF = 0x400454ca 
 47  TUNGETIFF = 0x800454d2 
 48  TUNGETFEATURES = 0x800454cf 
 49  IFF_TAP = 0x0002 
 50  IFF_NO_PI = 0x1000 
 51  IFF_ONE_QUEUE = 0x2000 
 52  IFF_VNET_HDR = 0x4000 
 53  IFF_MULTI_QUEUE = 0x0100 
 54   
 55   
56 -def _GetTunFeatures(fd, _ioctl=fcntl.ioctl):
57 """Retrieves supported TUN features from file descriptor. 58 59 @see: L{_ProbeTapVnetHdr} 60 61 """ 62 req = struct.pack("I", 0) 63 try: 64 buf = _ioctl(fd, TUNGETFEATURES, req) 65 except EnvironmentError, err: 66 logging.warning("ioctl(TUNGETFEATURES) failed: %s", err) 67 return None 68 else: 69 (flags, ) = struct.unpack("I", buf) 70 return flags
71 72
73 -def _ProbeTapVnetHdr(fd, _features_fn=_GetTunFeatures):
74 """Check whether to enable the IFF_VNET_HDR flag. 75 76 To do this, _all_ of the following conditions must be met: 77 1. TUNGETFEATURES ioctl() *must* be implemented 78 2. TUNGETFEATURES ioctl() result *must* contain the IFF_VNET_HDR flag 79 3. TUNGETIFF ioctl() *must* be implemented; reading the kernel code in 80 drivers/net/tun.c there is no way to test this until after the tap device 81 has been created using TUNSETIFF, and there is no way to change the 82 IFF_VNET_HDR flag after creating the interface, catch-22! However both 83 TUNGETIFF and TUNGETFEATURES were introduced in kernel version 2.6.27, 84 thus we can expect TUNGETIFF to be present if TUNGETFEATURES is. 85 86 @type fd: int 87 @param fd: the file descriptor of /dev/net/tun 88 89 """ 90 flags = _features_fn(fd) 91 92 if flags is None: 93 # Not supported 94 return False 95 96 result = bool(flags & IFF_VNET_HDR) 97 98 if not result: 99 logging.warning("Kernel does not support IFF_VNET_HDR, not enabling") 100 101 return result
102 103
104 -def _ProbeTapMqVirtioNet(fd, _features_fn=_GetTunFeatures):
105 """Check whether to enable the IFF_MULTI_QUEUE flag. 106 107 This flag was introduced in Linux kernel 3.8. 108 109 @type fd: int 110 @param fd: the file descriptor of /dev/net/tun 111 112 """ 113 flags = _features_fn(fd) 114 115 if flags is None: 116 # Not supported 117 return False 118 119 result = bool(flags & IFF_MULTI_QUEUE) 120 121 if not result: 122 logging.warning("Kernel does not support IFF_MULTI_QUEUE, not enabling") 123 124 return result
125 126
127 -def OpenTap(name="", features=None):
128 """Open a new tap device and return its file descriptor. 129 130 This is intended to be used by a qemu-type hypervisor together with the -net 131 tap,fd=<fd> or -net tap,fds=x:y:...:z command line parameter. 132 133 @type name: string 134 @param name: name for the TAP interface being created; if an empty 135 string is passed, the OS will generate a unique name 136 137 @type features: dict 138 @param features: A dict denoting whether vhost, vnet_hdr, mq 139 netdev features are enabled or not. 140 141 @return: (ifname, [tapfds], [vhostfds]) 142 @rtype: tuple 143 144 """ 145 tapfds = [] 146 vhostfds = [] 147 if features is None: 148 features = {} 149 vhost = features.get("vhost", False) 150 vnet_hdr = features.get("vnet_hdr", True) 151 _, virtio_net_queues = features.get("mq", (False, 1)) 152 153 for _ in range(virtio_net_queues): 154 try: 155 tapfd = os.open("/dev/net/tun", os.O_RDWR) 156 except EnvironmentError: 157 raise errors.HypervisorError("Failed to open /dev/net/tun") 158 159 flags = IFF_TAP | IFF_NO_PI 160 161 if vnet_hdr and _ProbeTapVnetHdr(tapfd): 162 flags |= IFF_VNET_HDR 163 164 # Check if it's ok to enable IFF_MULTI_QUEUE 165 if virtio_net_queues > 1 and _ProbeTapMqVirtioNet(tapfd): 166 flags |= IFF_MULTI_QUEUE 167 else: 168 flags |= IFF_ONE_QUEUE 169 170 # The struct ifreq ioctl request (see netdevice(7)) 171 ifr = struct.pack("16sh", name, flags) 172 173 try: 174 res = fcntl.ioctl(tapfd, TUNSETIFF, ifr) 175 except EnvironmentError, err: 176 raise errors.HypervisorError("Failed to allocate a new TAP device: %s" % 177 err) 178 179 if vhost: 180 # This is done regularly by the qemu process if vhost=on was passed with 181 # --netdev option. Still, in case of hotplug and if the process does not 182 # run with root privileges, we have to get the fds and pass them via 183 # SCM_RIGHTS prior to qemu using them. 184 try: 185 vhostfd = os.open("/dev/vhost-net", os.O_RDWR) 186 vhostfds.append(vhostfd) 187 except EnvironmentError: 188 raise errors.HypervisorError("Failed to open /dev/vhost-net") 189 190 tapfds.append(tapfd) 191 192 # Get the interface name from the ioctl 193 ifname = struct.unpack("16sh", res)[0].strip("\x00") 194 195 return (ifname, tapfds, vhostfds)
196