ovs mech: bind only if user request switchdev

In I77650be5f04775a72e2bdf694f93988825a84b72 we added
vnic_type direct to the ovs mechanism drivers supported
vnic_types. This cause problems when working with ovs and sriovnicswitch
mechanism drivers in that order.  In this case the ovs will bind
the direct port instead of the sriovnicswitch.

This change make ovs mech driver to bind the direct port only
if user requested --binding-profile '{"capabilities": ["switchdev"]}'
in the direct port if a user don't request this capability the SR-IOV
legacy NIC mode is used.

When enable-sriov-nic-features will be implemented in nova and
libvirt will expose the switchdev capability then nova will be
able to select a host which supports SR-IOV nic with switchdev
mode.

[1] - https://review.openstack.org/#/c/435954/11/specs/pike/approved/enable-sriov-nic-features.rst
[2] - https://www.redhat.com/archives/libvir-list/2017-August/msg00583.html

Closes-Bug: #1713590

Change-Id: I0b5f062bcbf02381bdf4f694fc039f9bb17a2db5
(cherry picked from commit b184558ab6)
This commit is contained in:
Moshe Levi 2017-08-30 18:35:48 +03:00 committed by Ihar Hrachyshka
parent 82db37397a
commit f6d956c1f5
5 changed files with 62 additions and 19 deletions

View File

@ -76,8 +76,17 @@ class SriovNicSwitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"network %(network)s", "network %(network)s",
{'port': context.current['id'], {'port': context.current['id'],
'network': context.network.current['id']}) 'network': context.network.current['id']})
profile = context.current.get(portbindings.PROFILE)
vnic_type = context.current.get(portbindings.VNIC_TYPE, vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL) portbindings.VNIC_NORMAL)
capabilities = []
if profile:
capabilities = profile.get('capabilities', [])
if (vnic_type == portbindings.VNIC_DIRECT and
'switchdev' in capabilities):
LOG.debug("Refusing to bind due to unsupported vnic_type: %s "
"with switchdev capability", portbindings.VNIC_DIRECT)
return
if vnic_type not in self.supported_vnic_types: if vnic_type not in self.supported_vnic_types:
LOG.debug("Refusing to bind due to unsupported vnic_type: %s", LOG.debug("Refusing to bind due to unsupported vnic_type: %s",
vnic_type) vnic_type)

View File

@ -20,6 +20,7 @@ from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry from neutron_lib.callbacks import registry
from neutron_lib import constants from neutron_lib import constants
from oslo_config import cfg from oslo_config import cfg
from oslo_log import log
from neutron.agent import securitygroups_rpc from neutron.agent import securitygroups_rpc
from neutron.plugins.common import constants as p_constants from neutron.plugins.common import constants as p_constants
@ -28,6 +29,7 @@ from neutron.plugins.ml2.drivers.openvswitch.agent.common \
import constants as a_const import constants as a_const
from neutron.services.qos.drivers.openvswitch import driver as ovs_qos_driver from neutron.services.qos.drivers.openvswitch import driver as ovs_qos_driver
LOG = log.getLogger(__name__)
IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall." IPTABLES_FW_DRIVER_FULL = ("neutron.agent.linux.iptables_firewall."
"OVSHybridIptablesFirewallDriver") "OVSHybridIptablesFirewallDriver")
@ -74,6 +76,20 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
"""Currently Openvswitch driver doesn't support vlan transparency.""" """Currently Openvswitch driver doesn't support vlan transparency."""
return False return False
def bind_port(self, context):
vnic_type = context.current.get(portbindings.VNIC_TYPE,
portbindings.VNIC_NORMAL)
profile = context.current.get(portbindings.PROFILE)
capabilities = []
if profile:
capabilities = profile.get('capabilities', [])
if (vnic_type == portbindings.VNIC_DIRECT and
'switchdev' not in capabilities):
LOG.debug("Refusing to bind due to unsupported vnic_type: %s with "
"no switchdev capability", portbindings.VNIC_DIRECT)
return
super(OpenvswitchMechanismDriver, self).bind_port(context)
def get_vif_type(self, context, agent, segment): def get_vif_type(self, context, agent, segment):
caps = agent['configurations'].get('ovs_capabilities', {}) caps = agent['configurations'].get('ovs_capabilities', {})
if (any(x in caps.get('iface_types', []) for x if (any(x in caps.get('iface_types', []) for x

View File

@ -43,11 +43,12 @@ class FakeNetworkContext(api.NetworkContext):
class FakePortContext(api.PortContext): class FakePortContext(api.PortContext):
def __init__(self, agent_type, agents, segments, def __init__(self, agent_type, agents, segments,
vnic_type=portbindings.VNIC_NORMAL, vnic_type=portbindings.VNIC_NORMAL,
original=None): original=None, profile=None):
self._agent_type = agent_type self._agent_type = agent_type
self._agents = agents self._agents = agents
self._network_context = FakeNetworkContext(segments) self._network_context = FakeNetworkContext(segments)
self._bound_vnic_type = vnic_type self._bound_vnic_type = vnic_type
self._bound_profile = profile
self._bound_segment_id = None self._bound_segment_id = None
self._bound_vif_type = None self._bound_vif_type = None
self._bound_vif_details = None self._bound_vif_details = None
@ -56,7 +57,8 @@ class FakePortContext(api.PortContext):
@property @property
def current(self): def current(self):
return {'id': PORT_ID, return {'id': PORT_ID,
portbindings.VNIC_TYPE: self._bound_vnic_type} portbindings.VNIC_TYPE: self._bound_vnic_type,
portbindings.PROFILE: self._bound_profile}
@property @property
def original(self): def original(self):

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License. # limitations under the License.
import mock
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib import constants from neutron_lib import constants
from neutron_lib.plugins.ml2 import api from neutron_lib.plugins.ml2 import api
@ -24,25 +25,16 @@ from neutron.plugins.ml2.drivers.mech_sriov.mech_driver \
from neutron.plugins.ml2.drivers.mech_sriov.mech_driver import mech_driver from neutron.plugins.ml2.drivers.mech_sriov.mech_driver import mech_driver
from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base from neutron.tests.unit.plugins.ml2 import _test_mech_agent as base
MELLANOX_CONNECTX3_PCI_INFO = '15b3:1004'
class TestFakePortContext(base.FakePortContext): class TestFakePortContext(base.FakePortContext):
def __init__(self, agent_type, agents, segments, def __init__(self, agent_type, agents, segments,
vnic_type=portbindings.VNIC_NORMAL, vnic_type=portbindings.VNIC_NORMAL,
profile={'pci_vendor_info': profile=None):
MELLANOX_CONNECTX3_PCI_INFO}):
super(TestFakePortContext, self).__init__(agent_type, super(TestFakePortContext, self).__init__(agent_type,
agents, agents,
segments, segments,
vnic_type) vnic_type=vnic_type,
self._bound_profile = profile profile=profile)
@property
def current(self):
return {'id': base.PORT_ID,
portbindings.VNIC_TYPE: self._bound_vnic_type,
portbindings.PROFILE: self._bound_profile}
def set_binding(self, segment_id, vif_type, vif_details, state): def set_binding(self, segment_id, vif_type, vif_details, state):
self._bound_segment_id = segment_id self._bound_segment_id = segment_id
@ -145,6 +137,18 @@ class SriovSwitchMechVnicTypeTestCase(SriovNicSwitchMechanismBaseTestCase):
self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT_PHYSICAL, self._check_vif_type_for_vnic_type(portbindings.VNIC_DIRECT_PHYSICAL,
portbindings.VIF_TYPE_HOSTDEV_PHY) portbindings.VIF_TYPE_HOSTDEV_PHY)
@mock.patch.object(mech_driver.SriovNicSwitchMechanismDriver,
'try_to_bind_segment_for_agent')
def test_vnic_type_direct_with_switchdev_cap(self, mocked_bind_segment):
profile = {'capabilities': ['switchdev']}
context = TestFakePortContext(self.AGENT_TYPE,
self.AGENTS,
self.VLAN_SEGMENTS,
portbindings.VNIC_DIRECT,
profile)
self.driver.bind_port(context)
mocked_bind_segment.assert_not_called()
class SriovSwitchMechVifDetailsTestCase(SriovNicSwitchMechanismBaseTestCase): class SriovSwitchMechVifDetailsTestCase(SriovNicSwitchMechanismBaseTestCase):
VLAN_SEGMENTS = [{api.ID: 'vlan_segment_id', VLAN_SEGMENTS = [{api.ID: 'vlan_segment_id',

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
import mock
from neutron_lib.api.definitions import portbindings from neutron_lib.api.definitions import portbindings
from neutron_lib.callbacks import events from neutron_lib.callbacks import events
from neutron_lib.callbacks import registry from neutron_lib.callbacks import registry
@ -243,12 +244,23 @@ class OpenvswitchMechanismDPDKTestCase(OpenvswitchMechanismBaseTestCase):
class OpenvswitchMechanismSRIOVTestCase(OpenvswitchMechanismBaseTestCase): class OpenvswitchMechanismSRIOVTestCase(OpenvswitchMechanismBaseTestCase):
def _make_port_ctx(self, agents): def _make_port_ctx(self, agents, profile=None):
segments = [{api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local'}] segments = [{api.ID: 'local_segment_id', api.NETWORK_TYPE: 'local'}]
return base.FakePortContext(self.AGENT_TYPE, agents, segments, return base.FakePortContext(self.AGENT_TYPE, agents, segments,
vnic_type=portbindings.VNIC_DIRECT) vnic_type=portbindings.VNIC_DIRECT,
profile=profile)
def test_get_vif_type(self): @mock.patch('neutron.plugins.ml2.drivers.mech_agent.'
'SimpleAgentMechanismDriverBase.bind_port')
def test_bind_port_sriov_legacy(self, mocked_bind_port):
context = self._make_port_ctx(self.AGENTS) context = self._make_port_ctx(self.AGENTS)
result = self.driver.get_vif_type(context, self.AGENTS[0], None) self.driver.bind_port(context)
self.assertEqual(self.VIF_TYPE, result) mocked_bind_port.assert_not_called()
@mock.patch('neutron.plugins.ml2.drivers.mech_agent.'
'SimpleAgentMechanismDriverBase.bind_port')
def test_bind_port_sriov_switchdev(self, mocked_bind_port):
profile = {'capabilities': ['switchdev']}
context = self._make_port_ctx(self.AGENTS, profile=profile)
self.driver.bind_port(context)
mocked_bind_port.assert_called()