Introduce a SR-IOV binding driver

The driver is for binding/unbinding SR-IOV port. Basically,
what it does is setting the vlan id for the VF interface.

Change-Id: Ife43b57a11c9aac9c0bece84adf719e62f708fda
Partial-Implements: blueprint sriov-binding
This commit is contained in:
Hongbin Lu 2017-09-03 21:54:33 -04:00 committed by Hongbin Lu
parent 917ee686c1
commit 48e3f4f91d
10 changed files with 194 additions and 11 deletions

View File

@ -14,7 +14,7 @@ from oslo_utils import importutils
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): segmentation_id=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,6 +29,7 @@ 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 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
@ -38,18 +39,20 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
driver = importutils.import_module(cfg.CONF.binding.driver) driver = importutils.import_module(cfg.CONF.binding.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)
def port_unbind(endpoint_id, neutron_port): def port_unbind(endpoint_id, neutron_port, **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 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
""" """
driver = importutils.import_module(cfg.CONF.binding.driver) driver = importutils.import_module(cfg.CONF.binding.driver)
return driver.port_unbind(endpoint_id, neutron_port) return driver.port_unbind(endpoint_id, neutron_port, **kwargs)

View File

@ -0,0 +1,74 @@
# 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_concurrency import processutils
from kuryr.lib.binding.drivers import utils
from kuryr.lib import constants
def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
segmentation_id=None, **kwargs):
"""Binds the Neutron port to the network interface on the host.
:param endpoint_id: the ID of the endpoint as string
:param 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
:param vm_port: the Nova instance port dictionary, as returned by
python-neutronclient. Container port under binding is
running inside this instance (either ipvlan/macvlan or
a subport)
:param segmentation_id: ID of the segment for container traffic isolation)
:param kwargs: Additional driver-specific arguments
:returns: the tuple of the names of the veth pair and the tuple of stdout
and stderr returned by processutils.execute invoked with the
executable script for binding
:raises: kuryr.common.exceptions.VethCreationFailure,
processutils.ProcessExecutionError
"""
pf_ifname = kwargs['pf_ifname']
vf_num = kwargs['vf_num']
mac_addr = port[utils.MAC_ADDRESS_KEY]
vlan = port[constants.VIF_DETAILS_KEY][constants.VIF_DETAILS_VLAN_KEY]
_set_vf_interface_vlan(pf_ifname, vf_num, mac_addr, vlan)
return None, None, ('', None)
def port_unbind(endpoint_id, neutron_port, **kwargs):
"""Unbinds the Neutron port from the network interface on the host.
:param endpoint_id: the ID of the Docker container as string
:param neutron_port: a port dictionary returned from python-neutronclient
:param kwargs: Additional driver-specific arguments
:returns: the tuple of stdout and stderr returned by processutils.execute
invoked with the executable script for unbinding
:raises: processutils.ProcessExecutionError, pyroute2.NetlinkError
"""
pf_ifname = kwargs['pf_ifname']
vf_num = kwargs['vf_num']
mac_addr = neutron_port[utils.MAC_ADDRESS_KEY]
_set_vf_interface_vlan(pf_ifname, vf_num, mac_addr)
return '', None
def _set_vf_interface_vlan(pf_ifname, vf_num, mac_addr, vlan=0):
exit_code = [0, 2, 254]
processutils.execute('ip', 'link', 'set', pf_ifname,
'vf', vf_num,
'mac', mac_addr,
'vlan', vlan,
run_as_root=True,
check_exit_code=exit_code)

View File

@ -21,7 +21,7 @@ IPVLAN_MODE_L2 = ifinfmsg.ifinfo.data_map['ipvlan'].modes['IPVLAN_MODE_L2']
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): segmentation_id=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
@ -35,6 +35,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
python-neutronclient. Container is running inside python-neutronclient. Container is running inside
this instance (either ipvlan/macvlan or a subport) this 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 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

View File

@ -20,7 +20,7 @@ MACVLAN_MODE_BRIDGE = 'bridge'
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): segmentation_id=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
@ -34,6 +34,7 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
python-neutronclient. Container is running inside python-neutronclient. Container is running inside
instance. instance.
:param segmentation_id: ID of the segment for container traffic isolation) :param segmentation_id: ID of the segment for container traffic isolation)
: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

View File

@ -31,11 +31,12 @@ def get_link_iface(port):
return link return link
def port_unbind(endpoint_id, neutron_port): def port_unbind(endpoint_id, neutron_port, **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 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

View File

@ -27,7 +27,7 @@ KIND = 'veth'
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): segmentation_id=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
@ -37,11 +37,12 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
endpoint is trying to join endpoint is trying to join
:param network: the Neutron network which the endpoint is trying to :param network: the Neutron network which the endpoint is trying to
join join
:param vm_port: the Nova instance dictionary, as returned by :param vm_port: the Nova instance port dictionary, as returned by
python-neutronclient. Container port under binding is python-neutronclient. Container port under binding is
running inside this instance (either ipvlan/macvlan or running inside this instance (either ipvlan/macvlan or
a subport) 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 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
@ -84,11 +85,12 @@ def port_bind(endpoint_id, port, subnets, network=None, vm_port=None,
return host_ifname, container_ifname, (stdout, stderr) return host_ifname, container_ifname, (stdout, stderr)
def port_unbind(endpoint_id, neutron_port): def port_unbind(endpoint_id, neutron_port, **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 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

View File

@ -18,7 +18,7 @@ KIND = 'vlan'
def port_bind(endpoint_id, port, subnets, network=None, def port_bind(endpoint_id, port, subnets, network=None,
vm_port=None, segmentation_id=None): vm_port=None, segmentation_id=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
@ -32,6 +32,7 @@ def port_bind(endpoint_id, port, subnets, network=None,
python-neutronclient. Container is running inside this python-neutronclient. Container is running inside this
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 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

View File

@ -28,4 +28,5 @@ DEFAULT_NETWORK_MTU = 1500
FALLBACK_VIF_TYPE = 'unbound' FALLBACK_VIF_TYPE = 'unbound'
UNBINDING_SUBCOMMAND = 'unbind' UNBINDING_SUBCOMMAND = 'unbind'
VIF_DETAILS_KEY = 'binding:vif_details' VIF_DETAILS_KEY = 'binding:vif_details'
VIF_DETAILS_VLAN_KEY = 'vlan'
VIF_TYPE_KEY = 'binding:vif_type' VIF_TYPE_KEY = 'binding:vif_type'

View File

@ -0,0 +1,94 @@
# 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 mock
from oslo_utils import uuidutils
from kuryr.lib.binding.drivers import hw_veb
from kuryr.lib import constants
from kuryr.lib import utils
from kuryr.tests.unit import base
mock_create = mock.MagicMock()
mock_interface = mock.MagicMock()
class TestHwVebDriver(base.TestCase):
"""Unit tests for hw_veb driver"""
@mock.patch('oslo_concurrency.processutils.execute',
return_value=('fake_stdout', 'fake_stderr'))
def test_port_bind(self, mock_execute):
fake_docker_endpoint_id = utils.get_hash()
fake_docker_network_id = utils.get_hash()
fake_port_id = uuidutils.generate_uuid()
fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
fake_vlan_id = 100
fake_vif_details = {constants.VIF_DETAILS_VLAN_KEY: fake_vlan_id}
fake_vif_type = "ovs"
fake_port = self._get_fake_port(
fake_docker_endpoint_id, fake_docker_network_id,
fake_port_id, constants.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
vif_details=fake_vif_details, vif_type=fake_vif_type)
fake_subnets = self._get_fake_subnets(
fake_docker_endpoint_id, fake_docker_network_id,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id)
fake_network = self._get_fake_networks(fake_docker_network_id)
fake_pf_ifname = 'eth13'
fake_vf_num = 1
hw_veb.port_bind(fake_docker_endpoint_id,
fake_port['port'],
fake_subnets['subnets'],
fake_network['networks'][0],
pf_ifname=fake_pf_ifname,
vf_num=fake_vf_num)
mock_execute.assert_called_once_with(
'ip', 'link', 'set', fake_pf_ifname,
'vf', fake_vf_num,
'mac', fake_port['port']['mac_address'],
'vlan', fake_vlan_id,
run_as_root=True,
check_exit_code=[0, 2, 254])
@mock.patch('oslo_concurrency.processutils.execute',
return_value=('fake_stdout', 'fake_stderr'))
def test_port_unbind(self, mock_execute):
fake_docker_endpoint_id = utils.get_hash()
fake_docker_network_id = utils.get_hash()
fake_port_id = uuidutils.generate_uuid()
fake_neutron_v4_subnet_id = uuidutils.generate_uuid()
fake_neutron_v6_subnet_id = uuidutils.generate_uuid()
fake_vif_type = "ovs"
fake_port = self._get_fake_port(
fake_docker_endpoint_id, fake_docker_network_id,
fake_port_id, constants.PORT_STATUS_ACTIVE,
fake_neutron_v4_subnet_id, fake_neutron_v6_subnet_id,
vif_type=fake_vif_type)
fake_pf_ifname = 'eth13'
fake_vf_num = 1
hw_veb.port_unbind(fake_docker_endpoint_id, fake_port['port'],
pf_ifname=fake_pf_ifname,
vf_num=fake_vf_num)
mock_execute.assert_called_once()
mock_execute.assert_called_once_with(
'ip', 'link', 'set', fake_pf_ifname,
'vf', fake_vf_num,
'mac', fake_port['port']['mac_address'],
'vlan', 0,
run_as_root=True,
check_exit_code=[0, 2, 254])

View File

@ -0,0 +1,5 @@
---
features:
- |
Introduce hw_veb binding driver. This driver can perform binding
of SR-IOV neutron port.