154 lines
5.8 KiB
Python
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
|