Allow multiple binding drivers

In before, only one binding driver is allowed. This patch introduces
a new config to support multiple binding drivers. The first use case
is to allow SR-IOV binding to be co-existed with other binding.

* Rename the config 'driver' to 'default_driver' in 'binding' group.
  This is for making it clear that it is allowed to have more than
  one type of bindings.
* Introduce a new config called 'enabled_drivers'.
* Allow client to pass a driver name to port_bind and port_unbind.
  If this parameter is None, kuryr will load the default driver.

Partial-Implements: blueprint sriov-binding
Change-Id: I14b23379de9f2459ba97d5d82dfdb51553370cb1
This commit is contained in:
Hongbin Lu 2017-10-01 17:53:50 +00:00 committed by Hongbin Lu
parent 4188f679d8
commit da736d115b
8 changed files with 81 additions and 9 deletions

View File

@ -12,9 +12,17 @@
from oslo_config import cfg from oslo_config import cfg
from oslo_utils import importutils from oslo_utils import importutils
from kuryr.lib import exceptions
def _verify_driver(driver):
if driver.__name__ not in cfg.CONF.binding.enabled_drivers:
raise exceptions.DriverNotEnabledException(
'Driver %s is not enabled' % driver.__name__)
def port_bind(endpoint_id, port, subnets, network=None, vm_port=None, def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
segmentation_id=None, **kwargs): segmentation_id=None, driver=None, **kwargs):
"""Binds the Neutron port to the network interface on the host. """Binds the Neutron port to the network interface on the host.
:param endpoint_id: the ID of the endpoint as string :param endpoint_id: the ID of the endpoint as string
@ -29,30 +37,39 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
port of a container which is running inside this Nova port of a container which is running inside this Nova
instance (either ipvlan/macvlan or a subport). instance (either ipvlan/macvlan or a subport).
:param segmentation_id: ID of the segment for container traffic isolation) :param segmentation_id: ID of the segment for container traffic isolation)
:param driver: the binding driver name
:param kwargs: Additional driver-specific arguments :param kwargs: Additional driver-specific arguments
:returns: the tuple of the names of the veth pair and the tuple of stdout :returns: the tuple of the names of the veth pair and the tuple of stdout
and stderr returned by processutils.execute invoked with the and stderr returned by processutils.execute invoked with the
executable script for binding executable script for binding
:raises: kuryr.common.exceptions.VethCreationFailure, :raises: kuryr.common.exceptions.VethCreationFailure,
kuryr.common.exceptions.DriverNotEnabledException,
processutils.ProcessExecutionError processutils.ProcessExecutionError
""" """
driver = importutils.import_module(cfg.CONF.binding.driver) driver = importutils.import_module(
driver or cfg.CONF.binding.default_driver)
_verify_driver(driver)
return driver.port_bind(endpoint_id, port, subnets, network=network, return driver.port_bind(endpoint_id, port, subnets, network=network,
vm_port=vm_port, vm_port=vm_port,
segmentation_id=segmentation_id, segmentation_id=segmentation_id,
**kwargs) **kwargs)
def port_unbind(endpoint_id, neutron_port, **kwargs): def port_unbind(endpoint_id, neutron_port, driver=None, **kwargs):
"""Unbinds the Neutron port from the network interface on the host. """Unbinds the Neutron port from the network interface on the host.
:param endpoint_id: the ID of the Docker container as string :param endpoint_id: the ID of the Docker container as string
:param neutron_port: a port dictionary returned from python-neutronclient :param neutron_port: a port dictionary returned from python-neutronclient
:param driver: the binding driver name
:param kwargs: Additional driver-specific arguments :param kwargs: Additional driver-specific arguments
:returns: the tuple of stdout and stderr returned by processutils.execute :returns: the tuple of stdout and stderr returned by processutils.execute
invoked with the executable script for unbinding invoked with the executable script for unbinding
:raises: processutils.ProcessExecutionError, pyroute2.NetlinkError :raises: processutils.ProcessExecutionError, pyroute2.NetlinkError,
kuryr.common.exceptions.DriverNotEnabledException,
""" """
driver = importutils.import_module(cfg.CONF.binding.driver) driver = importutils.import_module(
driver or cfg.CONF.binding.default_driver)
_verify_driver(driver)
return driver.port_unbind(endpoint_id, neutron_port, **kwargs) return driver.port_unbind(endpoint_id, neutron_port, **kwargs)

View File

@ -68,9 +68,13 @@ binding_opts = [
default='eth', default='eth',
help=_('The name prefix of the veth endpoint put inside the ' help=_('The name prefix of the veth endpoint put inside the '
'container.')), 'container.')),
cfg.StrOpt('driver', cfg.StrOpt('default_driver',
default='kuryr.lib.binding.drivers.veth', default='kuryr.lib.binding.drivers.veth',
deprecated_name='driver',
help=_('Driver to use for binding and unbinding ports.')), help=_('Driver to use for binding and unbinding ports.')),
cfg.ListOpt('enabled_drivers',
default=['kuryr.lib.binding.drivers.veth'],
help=_('Drivers to use for binding and unbinding ports.')),
cfg.StrOpt('link_iface', cfg.StrOpt('link_iface',
default='', default='',
help=_('Specifies the name of the Nova instance interface to ' help=_('Specifies the name of the Nova instance interface to '

View File

@ -118,3 +118,11 @@ class AddressInUseException(KuryrException):
This exception is thrown when a specific address is requested and the This exception is thrown when a specific address is requested and the
requested ip address already exists and is used. requested ip address already exists and is used.
""" """
class DriverNotEnabledException(KuryrException):
"""Exception represents the binding driver is not enabled.
This exception is thrown when kuryr tries to load a specific binding driver
but the driver is not enabled.
"""

View File

@ -22,7 +22,7 @@ _driver = ""
def _get_driver(): def _get_driver():
global _driver global _driver
if not _driver: if not _driver:
driver_name = cfg.CONF.binding.driver.rsplit('.', 1)[1] driver_name = cfg.CONF.binding.default_driver.rsplit('.', 1)[1]
# REVISIT(vikasc): Need to remove this if check # REVISIT(vikasc): Need to remove this if check
if driver_name == 'vlan': if driver_name == 'vlan':

View File

@ -0,0 +1,32 @@
# 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.
from oslo_config import cfg
from oslo_utils import importutils
from kuryr.lib import binding
from kuryr.lib import exceptions
from kuryr.tests.unit import base
class TestBinding(base.TestCase):
"""Unit tests for binding module"""
def test__verify_driver(self):
cfg.CONF.set_override('enabled_drivers',
['kuryr.lib.binding.drivers.veth'],
group='binding')
driver = importutils.import_module('kuryr.lib.binding.drivers.veth')
binding._verify_driver(driver) # assert no exception raise
driver = importutils.import_module('kuryr.lib.binding.drivers.vlan')
self.assertRaises(exceptions.DriverNotEnabledException,
binding._verify_driver, driver)

View File

@ -29,7 +29,7 @@ class VlanSegmentationDriverTest(base.TestCase):
def setUp(self): def setUp(self):
super(VlanSegmentationDriverTest, self).setUp() super(VlanSegmentationDriverTest, self).setUp()
cfg.CONF.binding.driver = 'kuryr.lib.binding.drivers.vlan' cfg.CONF.binding.default_driver = 'kuryr.lib.binding.drivers.vlan'
def test_allocate_segmentation_id(self): def test_allocate_segmentation_id(self):
vlan_seg_driver = vlan.SegmentationDriver() vlan_seg_driver = vlan.SegmentationDriver()

View File

@ -30,4 +30,4 @@ class ConfigurationTest(base.TestCase):
self.assertEqual('baremetal', self.assertEqual('baremetal',
cfg.CONF.deployment_type) cfg.CONF.deployment_type)
self.assertEqual('kuryr.lib.binding.drivers.veth', self.assertEqual('kuryr.lib.binding.drivers.veth',
cfg.CONF.binding.driver) cfg.CONF.binding.default_driver)

View File

@ -0,0 +1,11 @@
---
features:
- |
Add support for multiple binding drivers. Introduce a new config
called 'enabled_drivers' which specifies a list of binding drivers
allowed to use.
deprecations:
- |
Rename the config 'driver' to 'default_driver' in 'binding' group.
This is for making it clear that it is allowed to have more than
one type of bindings.