kuryr-libnetwork/kuryr_libnetwork/port_driver/drivers/sriov.py

154 lines
5.8 KiB
Python

# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import glob
import os
import re
from kuryr.lib import binding
from kuryr.lib import exceptions
from kuryr_libnetwork import constants as const
from kuryr_libnetwork.port_driver import driver
def get_ifname_by_pci_address(pci_addr, pf_interface=False):
"""Get the interface name based on a VF's pci address.
The returned interface name is either the parent PF's or that of the VF
itself based on the argument of pf_interface.
"""
dev_path = _get_sysfs_netdev_path(pci_addr, pf_interface)
try:
dev_info = os.listdir(dev_path)
return dev_info.pop()
except Exception:
raise exceptions.KuryrException(
"PCI device %s not found" % pci_addr)
def _get_sysfs_netdev_path(pci_addr, pf_interface):
"""Get the sysfs path based on the PCI address of the device.
Assumes a networking device - will not check for the existence of the path.
"""
if pf_interface:
return "/sys/bus/pci/devices/%s/physfn/net" % pci_addr
return "/sys/bus/pci/devices/%s/net" % pci_addr
def get_vf_num_by_pci_address(pci_addr):
"""Get the VF number based on a VF's pci address
A VF is associated with an VF number, which ip link command uses to
configure it. This number can be obtained from the PCI device filesystem.
"""
VIRTFN_RE = re.compile("virtfn(\d+)")
virtfns_path = "/sys/bus/pci/devices/%s/physfn/virtfn*" % (pci_addr)
vf_num = None
try:
for vf_path in glob.iglob(virtfns_path):
if re.search(pci_addr, os.readlink(vf_path)):
t = VIRTFN_RE.search(vf_path)
vf_num = t.group(1)
break
except Exception:
pass
if vf_num is None:
raise exceptions.KuryrException(
"PCI device %s not found" % pci_addr)
return vf_num
class SriovDriver(driver.Driver):
"""Driver supporting SR-IOV on Bare Metal"""
BINDING_DRIVERS = ('hw_veb',)
def get_supported_bindings(self):
"""Returns a tuple of supported binding driver names for the driver.
:returns: a tuple of strings
"""
return self.BINDING_DRIVERS
def get_default_network_id(self):
"""Returns a Neutron network ID as per driver logic, if any.
This driver does not make use of any specific network and will thus
return None.
:returns: None
"""
return None
def create_host_iface(self, endpoint_id, neutron_port, subnets,
network=None):
"""Instantiates a host interface and bind it to the host.
:param endpoint_id: the ID of the endpoint as string
:param neutron_port: the container Neutron port dictionary as returned
by python-neutronclient
:param subnets: an iterable of all the Neutron subnets which the
endpoint is trying to join
:param network: the Neutron network which the endpoint is trying
to join
:returns: the tuple of stdout and stderr returned by
processutils.execute invoked with the executable script for
unbinding
:raises: kuryr.lib.exceptions.BindingNotSupportedFailure
processutils.ProcessExecutionError
"""
binding_driver = 'kuryr.lib.binding.drivers.hw_veb'
pci_addr = neutron_port[const.BINDING_PROFILE]['pci_slot']
pf_ifname = get_ifname_by_pci_address(pci_addr,
pf_interface=True)
vf_num = get_vf_num_by_pci_address(pci_addr)
_, _, (stdout, stderr) = binding.port_bind(
endpoint_id, neutron_port, subnets, pf_ifname=pf_ifname,
vf_num=vf_num, driver=binding_driver)
return (stdout, stderr)
def delete_host_iface(self, endpoint_id, neutron_port):
"""Deletes a host interface after unbinding it from the host.
The host veth interface associated to the Neutron port will be unbound
from its vitual bridge and deleted by delegating to the selected
kuryr-lib driver.
:param endpoint_id: the ID of the Docker container as string
:param neutron_port: a port dictionary returned from
python-neutronclient
:returns: the tuple of stdout and stderr returned by
processutils.execute invoked with the executable script for
unbinding
:raises: processutils.ProcessExecutionError
"""
binding_driver = 'kuryr.lib.binding.drivers.hw_veb'
pci_addr = neutron_port[const.BINDING_PROFILE]['pci_slot']
pf_ifname = get_ifname_by_pci_address(pci_addr,
pf_interface=True)
vf_num = get_vf_num_by_pci_address(pci_addr)
return binding.port_unbind(endpoint_id, neutron_port,
pf_ifname=pf_ifname,
vf_num=vf_num, driver=binding_driver)
def get_container_iface_name(self, neutron_port):
"""Returns interface name of a container in the default namespace.
:param neutron_port_id: The ID of a neutron port as string
:returns: interface name as string
"""
pci_addr = neutron_port[const.BINDING_PROFILE]['pci_slot']
vf_ifname = get_ifname_by_pci_address(pci_addr)
return vf_ifname