Add Smart NIC representor port to integration bridge

In case of Smart NIC vNIC type neutron should mimic nova-compute
that plug the port to the ovs bridge.

Extend the Neutron OVS mechanism driver and Neutron OVS Agent to bind
the Neutron port for the baremetal host with Smart NIC. This will allow
the Neutron OVS Agent to configure the pipeline of the OVS running on
the Smart NIC and leverage the pipeline features such as: VXLAN,
Security Groups and ARP Responder.

Story: #2003346
Closes-Bug: #1785608
Change-Id: I6d520d3bac2e9ceb30b5b6197c6eb0f958cc3659
This commit is contained in:
Hamdy Khader 2018-07-26 17:40:53 +03:00
parent 0c87350a94
commit b4243ad3f7
16 changed files with 414 additions and 29 deletions

View File

@ -62,6 +62,7 @@ openstacksdk==0.11.2
os-client-config==1.28.0
os-ken==0.3.0
os-service-types==1.2.0
os-vif==1.15.1
os-xenapi==0.3.1
osc-lib==1.8.0
oslo.cache==1.26.0

View File

@ -113,6 +113,7 @@ class PluginApi(object):
1.5 - Support update_device_list and
get_devices_details_list_and_failed_devices
1.6 - Support get_network_details
1.7 - Support get_ports_by_vnic_type_and_host
'''
def __init__(self, topic):
@ -192,6 +193,11 @@ class PluginApi(object):
return cctxt.call(context, 'tunnel_sync', tunnel_ip=tunnel_ip,
tunnel_type=tunnel_type, host=host)
def get_ports_by_vnic_type_and_host(self, context, vnic_type, host):
cctxt = self.client.prepare(version='1.7')
return cctxt.call(context, 'get_ports_by_vnic_type_and_host',
vnic_type=vnic_type, host=host)
def create_cache_for_l2_agent():
"""Create a push-notifications cache for L2 agent related resources."""
@ -336,6 +342,7 @@ class CacheBackedPluginApi(PluginApi):
dialect=netaddr.mac_unix_expanded))
entry = {
'device': device,
'device_id': port_obj.device_id,
'network_id': port_obj.network_id,
'port_id': port_obj.id,
'mac_address': mac_addr,
@ -355,6 +362,8 @@ class CacheBackedPluginApi(PluginApi):
'qos_policy_id': port_obj.qos_policy_id,
'network_qos_policy_id': net_qos_policy_id,
'profile': binding.profile,
'vif_type': binding.vif_type,
'vnic_type': binding.vnic_type,
'security_groups': list(port_obj.security_group_ids)
}
LOG.debug("Returning: %s", entry)

View File

@ -166,7 +166,9 @@ agent_opts = [
"outgoing IP packet carrying GRE/VXLAN tunnel.")),
cfg.StrOpt('agent_type', default=n_const.AGENT_TYPE_OVS,
deprecated_for_removal=True,
help=_("Selects the Agent Type reported"))
help=_("Selects the Agent Type reported.")),
cfg.BoolOpt('baremetal_smartnic', default=False,
help=_("Enable the agent to process Smart NIC ports.")),
]

View File

@ -531,3 +531,13 @@ class Port(base.NeutronDbObject):
ml2_models.PortBinding.vif_type == binding_type,
ml2_models.PortBinding.host == host)
return [cls._load_object(context, db_obj) for db_obj in query.all()]
@classmethod
def get_ports_by_vnic_type_and_host(
cls, context, vnic_type, host):
query = context.session.query(models_v2.Port).join(
ml2_models.PortBinding)
query = query.filter(
ml2_models.PortBinding.vnic_type == vnic_type,
ml2_models.PortBinding.host == host)
return [cls._load_object(context, db_obj) for db_obj in query.all()]

View File

@ -105,6 +105,11 @@ class AgentMechanismDriverBase(api.MechanismDriver):
for agent in agents:
LOG.debug("Checking agent: %s", agent)
if agent['alive']:
if (vnic_type == portbindings.VNIC_SMARTNIC and not
agent['configurations'].get('baremetal_smartnic')):
LOG.debug('Agent on host %s can not bind SmartNIC '
'port %s', agent['host'], context.current['id'])
continue
for segment in context.segments_to_bind:
if self.try_to_bind_segment_for_agent(context, segment,
agent):

View File

@ -34,6 +34,10 @@ from neutron_lib import context
from neutron_lib.placement import utils as place_utils
from neutron_lib.plugins import utils as plugin_utils
from neutron_lib.utils import helpers
import os_vif
from os_vif.objects import instance_info as vif_instance_object
from os_vif.objects import network as vif_network_object
from os_vif.objects import vif as vif_obj
from oslo_config import cfg
from oslo_log import log as logging
import oslo_messaging
@ -128,7 +132,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# 1.3 Added param devices_to_update to security_groups_provider_updated
# 1.4 Added support for network_update
# 1.5 Added binding_activate and binding_deactivate
target = oslo_messaging.Target(version='1.5')
# 1.7 Add support for smartnic ports
target = oslo_messaging.Target(version='1.7')
def __init__(self, bridge_classes, ext_manager, conf=None):
'''Constructor.
@ -182,6 +187,8 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.deactivated_bindings = set()
# Stores the port IDs whose binding has been activated
self.activated_bindings = set()
# Stores smartnic ports update/remove
self.updated_smartnic_ports = list()
self.network_ports = collections.defaultdict(set)
# keeps association between ports and ofports to detect ofport change
@ -303,7 +310,9 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
'ovs_capabilities': self.ovs.capabilities,
'vhostuser_socket_dir':
ovs_conf.vhostuser_socket_dir,
portbindings.OVS_HYBRID_PLUG: hybrid_plug},
portbindings.OVS_HYBRID_PLUG: hybrid_plug,
'baremetal_smartnic':
self.conf.AGENT.baremetal_smartnic},
'resource_versions': resources.LOCAL_RESOURCE_VERSIONS,
'agent_type': agent_conf.agent_type,
'start_flag': True}
@ -320,6 +329,9 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.catch_sigterm = False
self.catch_sighup = False
if self.conf.AGENT.baremetal_smartnic:
os_vif.initialize()
# The initialization is complete; we can start receiving messages
self.connection.consume_in_threads()
@ -474,6 +486,79 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# are processed in the same order as the relevant API requests
self.updated_ports.add(port['id'])
if not self.conf.AGENT.baremetal_smartnic:
return
# In case of smart-nic port, add smart-nic representor port to
# the integration bridge.
port_data = (self.plugin_rpc.remote_resource_cache
.get_resource_by_id(resources.PORT, port['id']))
if not port_data:
LOG.warning('Failed to get port details, port id: %s', port['id'])
return
for port_binding in port_data.get('bindings', []):
if port_binding['vnic_type'] == portbindings.VNIC_SMARTNIC:
if port_binding['host'] == self.conf.host:
self._add_port_to_updated_smartnic_ports(port_data,
port_binding)
else:
# The port doesn't belong to this Smart NIC,
# the reason for this could be multi Smart NIC
# setup.
LOG.info("Smart NIC port %(port_id)s does not belong "
"to host %(host)s",
{'port_id': port['id'],
'host': self.conf.host})
def treat_smartnic_port(self, smartnic_port_data):
mac = smartnic_port_data['mac']
vm_uuid = smartnic_port_data['vm_uuid']
rep_port = smartnic_port_data['iface_name']
iface_id = smartnic_port_data['iface_id']
vif_type = smartnic_port_data['vif_type']
instance_info = vif_instance_object.InstanceInfo(uuid=vm_uuid)
vif = self._get_vif_object(iface_id, rep_port, mac)
try:
if vif_type == portbindings.VIF_TYPE_OVS:
os_vif.plug(vif, instance_info)
elif vif_type == portbindings.VIF_TYPE_UNBOUND:
os_vif.unplug(vif, instance_info)
else:
LOG.error("Unexpected vif_type:%(vif_type)s for "
"%(vnic_type)s port:%(port_id)s",
{'vnic_type': portbindings.VNIC_SMARTNIC,
'vif_type': vif_type,
'port_id': iface_id})
except Exception as e:
LOG.error("Failed to treat %(vnic_type)s port:%(port_id)s , "
"error:%(error)s",
{'vnic_type': portbindings.VNIC_SMARTNIC,
'port_id': iface_id,
'error': e})
def _get_vif_object(self, iface_id, rep_port, mac):
network = vif_network_object.Network(
bridge=self.conf.OVS.integration_bridge)
port_profile = vif_obj.VIFPortProfileOpenVSwitch(
interface_id=iface_id, create_port=True)
return vif_obj.VIFOpenVSwitch(
vif_name=rep_port, plugin='ovs', port_profile=port_profile,
network=network, address=str(mac))
def _add_port_to_updated_smartnic_ports(self, port_data, port_binding):
local_link = port_binding['profile']['local_link_information']
if local_link:
iface_name = local_link[0]['port_id']
self.updated_smartnic_ports.append({
'mac': port_data['mac_address'],
'vm_uuid': port_data['device_id'],
'iface_name': iface_name,
'iface_id': port_data['id'],
'vif_type': port_binding['vif_type']})
def port_delete(self, context, **kwargs):
port_id = kwargs.get('port_id')
self.deleted_ports.add(port_id)
@ -536,6 +621,17 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
# more secure
self.sg_agent.remove_devices_filter(deleted_ports)
def process_smartnic_ports(self):
smartnic_ports = self.plugin_rpc.get_ports_by_vnic_type_and_host(
self.context, portbindings.VNIC_SMARTNIC, self.conf.host)
ports = self.int_br.get_vif_port_set()
for smartnic_port in smartnic_ports:
if smartnic_port['id'] not in ports:
self._add_port_to_updated_smartnic_ports(
smartnic_port,
{'profile': smartnic_port['binding:profile'],
'vif_type': smartnic_port['binding:vif_type']})
def process_deactivated_bindings(self, port_info):
# don't try to deactivate bindings for removed ports since they are
# already gone
@ -1972,6 +2068,7 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
self.deleted_ports or
self.deactivated_bindings or
self.activated_bindings or
self.updated_smartnic_ports or
self.sg_agent.firewall_refresh_needed())
def _port_info_has_changes(self, port_info):
@ -2249,6 +2346,16 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
"starting polling. Elapsed:%(elapsed).3f",
{'iter_num': self.iter_num,
'elapsed': time.time() - start})
if self.conf.AGENT.baremetal_smartnic:
if sync:
self.process_smartnic_ports()
updated_smartnic_ports_copy = (
self.updated_smartnic_ports)
self.updated_smartnic_ports = list()
for port_data in updated_smartnic_ports_copy:
self.treat_smartnic_port(port_data)
# Save updated ports dict to perform rollback in
# case resync would be needed, and then clear
# self.updated_ports. As the greenthread should not yield

View File

@ -77,7 +77,9 @@ class OpenvswitchMechanismDriver(mech_agent.SimpleAgentMechanismDriverBase):
# SimpleAgentMechanismDriverBase. By that e blacklisting and validation
# of the vnic_types would be available for all mechanism drivers.
self.supported_vnic_types = self.blacklist_supported_vnic_types(
vnic_types=[portbindings.VNIC_NORMAL, portbindings.VNIC_DIRECT],
vnic_types=[portbindings.VNIC_NORMAL,
portbindings.VNIC_DIRECT,
portbindings.VNIC_SMARTNIC],
blacklist=cfg.CONF.OVS_DRIVER.vnic_type_blacklist
)
LOG.info("%s's supported_vnic_types: %s",

View File

@ -2517,3 +2517,11 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
db.clear_binding_levels(context,
port_id=port_id,
host=host)
@db_api.retry_if_session_inactive()
def get_ports_by_vnic_type_and_host(self, context, **kwargs):
host = kwargs['host']
vnic_type = kwargs['vnic_type']
ports = ports_obj.Port.get_ports_by_vnic_type_and_host(
context, vnic_type, host)
return [self._make_port_dict(port.db_obj) for port in ports]

View File

@ -52,7 +52,8 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
# 1.5 Support update_device_list and
# get_devices_details_list_and_failed_devices
# 1.6 Support get_network_details
target = oslo_messaging.Target(version='1.6')
# 1.7 Support get_ports_by_vnic_type_and_host
target = oslo_messaging.Target(version='1.7')
def __init__(self, notifier, type_manager):
self.setup_tunnel_callback_mixin(notifier, type_manager)
@ -399,6 +400,11 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
'devices_down': devices_down,
'failed_devices_down': failed_devices_down}
def get_ports_by_vnic_type_and_host(self, rpc_context, vnic_type, host):
plugin = directory.get_plugin()
return plugin.get_ports_by_vnic_type_and_host(
rpc_context, vnic_type=vnic_type, host=host)
class AgentNotifierApi(dvr_rpc.DVRAgentRpcApiMixin,
sg_rpc.SecurityGroupAgentRpcApiMixin,

View File

@ -44,6 +44,11 @@ class AgentRPCPluginApi(base.BaseTestCase):
func_obj = getattr(agent, method)
if method == 'tunnel_sync':
actual_val = func_obj(ctxt, 'fake_tunnel_ip')
elif method == 'get_ports_by_vnic_type_and_host':
actual_val = func_obj(ctxt, 'fake_vnic_type', 'fake_host')
mock_call.assert_called_once_with(
ctxt, 'get_ports_by_vnic_type_and_host',
host='fake_host', vnic_type='fake_vnic_type')
else:
actual_val = func_obj(ctxt, 'fake_device', 'fake_agent_id')
self.assertEqual(actual_val, expect_val)
@ -63,6 +68,9 @@ class AgentRPCPluginApi(base.BaseTestCase):
def test_tunnel_sync(self):
self._test_rpc_call('tunnel_sync')
def test_get_ports_by_vnic_type_and_host(self):
self._test_rpc_call('get_ports_by_vnic_type_and_host')
class AgentPluginReportState(base.BaseTestCase):
def test_plugin_report_state_use_call(self):
@ -191,6 +199,7 @@ class TestCacheBackedPluginApi(base.BaseTestCase):
segments=[self._segment])
self._port = ports.Port(
id=self._port_id, network_id=self._network_id,
device_id='vm_uuid',
mac_address=netaddr.EUI('fa:16:3e:ec:c7:d9'), admin_state_up=True,
security_group_ids=set([uuidutils.generate_uuid()]),
fixed_ips=[], allowed_address_pairs=[],
@ -198,7 +207,9 @@ class TestCacheBackedPluginApi(base.BaseTestCase):
bindings=[ports.PortBinding(port_id=self._port_id,
host='host1',
status=constants.ACTIVE,
profile={})],
profile={},
vif_type='vif_type',
vnic_type='vnic_type')],
binding_levels=[ports.PortBindingLevel(port_id=self._port_id,
host='host1',
level=0,

View File

@ -478,3 +478,20 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
ports.Port.get_ports_ids_by_security_groups(
self.context, security_group_ids=(sg_id, ),
excluded_device_owners=filter_owner)))
def test_get_ports_by_vnic_type_and_host(self):
port1 = self._create_test_port()
ports.PortBinding(
self.context,
host='host1', port_id=port1.id, status='ACTIVE',
vnic_type='vnic_type1', vif_type='vif_type1').create()
port2 = self._create_test_port()
ports.PortBinding(
self.context,
host='host1', port_id=port2.id, status='ACTIVE',
vnic_type='vnic_type2', vif_type='vif_type1').create()
self.assertEqual(1, len(
ports.Port.get_ports_by_vnic_type_and_host(
self.context, 'vnic_type1', 'host1')))

View File

@ -16,10 +16,14 @@ import sys
import time
import mock
import netaddr
from neutron_lib.agent import constants as agent_consts
from neutron_lib.api.definitions import portbindings
from neutron_lib.api.definitions import provider_net
from neutron_lib import constants as n_const
from neutron_lib import rpc as n_rpc
import os_vif
from os_vif.objects import instance_info as vif_instance_object
from oslo_config import cfg
from oslo_log import log
import oslo_messaging
@ -31,6 +35,8 @@ from neutron.agent.common import ovs_lib
from neutron.agent.common import polling
from neutron.agent.common import utils
from neutron.agent.linux import ip_lib
from neutron.objects.ports import Port
from neutron.objects.ports import PortBinding
from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
from neutron.plugins.ml2.drivers.openvswitch.agent.common import constants
from neutron.plugins.ml2.drivers.openvswitch.agent import ovs_neutron_agent \
@ -967,7 +973,10 @@ class TestOvsNeutronAgent(object):
'failed_devices_up': [],
'failed_devices_down': []}):
with mock.patch.object(self.agent, 'port_unbound') as port_unbound:
self.assertFalse(self.agent.treat_devices_removed([{}]))
with mock.patch.object(self.agent.int_br,
'get_vif_port_by_id',
return_value=None):
self.assertFalse(self.agent.treat_devices_removed([{}]))
self.assertTrue(port_unbound.called)
def test_treat_devices_removed_unbinds_port(self):
@ -985,9 +994,14 @@ class TestOvsNeutronAgent(object):
'failed_devices_up': [],
'failed_devices_down': [
dev_mock]}):
failed_devices = {'added': set(), 'removed': set()}
failed_devices['removed'] = self.agent.treat_devices_removed([{}])
self.assertEqual(set([dev_mock]), failed_devices.get('removed'))
with mock.patch.object(self.agent.int_br,
'get_vif_port_by_id',
return_value=None):
failed_devices = {'added': set(), 'removed': set()}
failed_devices['removed'] = \
self.agent.treat_devices_removed([{}])
self.assertEqual(set([dev_mock]),
failed_devices.get('removed'))
def test_treat_devices_removed_ext_delete_port(self):
port_id = 'fake-id'
@ -999,10 +1013,12 @@ class TestOvsNeutronAgent(object):
'failed_devices_up': [],
'failed_devices_down': []})
m_unbound = mock.patch.object(self.agent, 'port_unbound')
with m_delete as delete, m_rpc, m_unbound:
self.agent.treat_devices_removed([port_id])
delete.assert_called_with(mock.ANY, {'port_id': port_id})
with mock.patch.object(self.agent.int_br,
'get_vif_port_by_id',
return_value=None):
self.agent.treat_devices_removed([port_id])
delete.assert_called_with(mock.ANY, {'port_id': port_id})
def test_treat_vif_port_shut_down_port(self):
details = mock.MagicMock()
@ -1176,15 +1192,54 @@ class TestOvsNeutronAgent(object):
self.assertTrue(self.agent.fullsync)
def test_port_update(self):
port = {"id": TEST_PORT_ID1,
"network_id": TEST_NETWORK_ID1,
"admin_state_up": False}
self.agent.port_update("unused_context",
port=port,
network_type="vlan",
segmentation_id="1",
physical_network="physnet")
self.assertEqual(set([TEST_PORT_ID1]), self.agent.updated_ports)
port_arg = {"id": TEST_PORT_ID1}
with mock.patch.object(self.agent.plugin_rpc.remote_resource_cache,
"get_resource_by_id") as mocked_resource:
port = Port()
port['mac_address'] = netaddr.EUI(FAKE_MAC)
port['device_id'] = '0'
port_bind = PortBinding()
port_bind['host'] = 'host'
port_bind['vnic_type'] = 'normal'
port.bindings = [port_bind]
mocked_resource.return_value = port
self.agent.port_update("unused_context",
port=port_arg,
network_type="vlan",
segmentation_id="1",
physical_network="physnet")
self.assertEqual(set([TEST_PORT_ID1]), self.agent.updated_ports)
self.assertEqual([], self.agent.updated_smartnic_ports)
def test_port_update_smartnic(self):
cfg.CONF.set_default('baremetal_smartnic', True, group='AGENT')
port_arg = {"id": TEST_PORT_ID1}
with mock.patch.object(self.agent.plugin_rpc.remote_resource_cache,
"get_resource_by_id") as mocked_resource:
port = Port()
port['id'] = 'd850ed99-5f46-47bc-8c06-86d9d519c46a'
port['mac_address'] = netaddr.EUI(FAKE_MAC)
port['device_id'] = '0'
port_bind = PortBinding()
port_bind['host'] = 'host'
port_bind['vnic_type'] = portbindings.VNIC_SMARTNIC
port_bind['vif_type'] = 'ovs'
port_bind['profile'] = {
'local_link_information': [{'port_id': 'rep_port'}]}
port.bindings = [port_bind]
mocked_resource.return_value = port
self.agent.port_update("unused_context",
port=port_arg)
expected_smartnic_data = {
'mac': port['mac_address'],
'vm_uuid': port['device_id'],
'iface_name': 'rep_port',
'iface_id': port['id'],
'vif_type': port_bind['vif_type']
}
self.assertEqual({TEST_PORT_ID1}, self.agent.updated_ports)
self.assertEqual([expected_smartnic_data],
self.agent.updated_smartnic_ports)
def test_port_delete_after_update(self):
"""Make sure a port is not marked for delete and update."""
@ -2421,6 +2476,133 @@ class TestOvsNeutronAgent(object):
self.agent._update_network_segmentation_id(network)
mock_update_segid.assert_not_called()
def _test_treat_smartnic_port(self, vif_type):
vm_uuid = "407a79e0-e0be-4b7d-92a6-513b2161011b"
iface_id = "407a79e0-e0be-4b7d-92a6-513b2161011c"
rep_port = 'rep0-0'
mac = FAKE_MAC
smartnic_data = {
'mac': mac,
'vm_uuid': vm_uuid,
'iface_name': rep_port,
'iface_id': iface_id,
'vif_type': vif_type}
cfg.CONF.set_default('baremetal_smartnic', True, group='AGENT')
agent = self._make_agent()
instance_info = vif_instance_object.InstanceInfo(uuid=vm_uuid)
vif = agent._get_vif_object(iface_id, rep_port, mac)
with mock.patch.object(os_vif, 'plug') as plug_mock, \
mock.patch.object(os_vif, 'unplug') as unplug_mock, \
mock.patch('os_vif.objects.instance_info.InstanceInfo',
return_value=instance_info), \
mock.patch.object(agent, '_get_vif_object',
return_value=vif):
agent.treat_smartnic_port(smartnic_data)
if vif_type == 'ovs':
plug_mock.assert_called_once_with(vif, instance_info)
else:
unplug_mock.assert_called_once_with(vif, instance_info)
def test_treat_smartnic_port_add(self):
self._test_treat_smartnic_port('ovs')
def test_treat_smartnic_port_remove(self):
self._test_treat_smartnic_port('unbound')
def test_process_smartnic_ports(self):
port_id_int_br = "407a79e0-e0be-4b7d-92a6-513b2161011a"
vm_uuid = "407a79e0-e0be-4b7d-92a6-513b2161011b"
iface_id = "407a79e0-e0be-4b7d-92a6-513b2161011c"
rep_port = 'rep0-0'
mac = FAKE_MAC
vif_type = "ovs"
EXISTING_PORT = {'id': port_id_int_br}
PORT_TO_PROCESS = {
'binding:profile': {'local_link_information': [
{'hostname': 'host1', 'port_id': rep_port}]},
'mac_address': FAKE_MAC,
'device_id': vm_uuid,
'id': iface_id,
'binding:vif_type': vif_type
}
with mock.patch.object(self.agent.int_br,
'get_vif_port_set',
return_value={port_id_int_br}),\
mock.patch.object(self.agent.plugin_rpc,
"get_ports_by_vnic_type_and_host",
return_value=[EXISTING_PORT,
PORT_TO_PROCESS]):
smartnic_data = {
'mac': mac,
'vm_uuid': vm_uuid,
'iface_name': rep_port,
'iface_id': iface_id,
'vif_type': vif_type}
self.agent.process_smartnic_ports()
self.assertEqual([smartnic_data],
self.agent.updated_smartnic_ports)
def test_add_port_to_updated_smartnic_ports_port_as_dict(self):
vm_uuid = "407a79e0-e0be-4b7d-92a6-513b2161011b"
iface_id = "407a79e0-e0be-4b7d-92a6-513b2161011c"
rep_port = 'rep0-0'
mac = FAKE_MAC
vif_type = "ovs"
PORT_TO_PROCESS = {
'binding:profile': {'local_link_information': [
{'hostname': 'host1', 'port_id': rep_port}]},
'mac_address': FAKE_MAC,
'device_id': vm_uuid,
'id': iface_id,
'binding:vif_type': vif_type
}
self.agent._add_port_to_updated_smartnic_ports(
PORT_TO_PROCESS,
{'profile': PORT_TO_PROCESS['binding:profile'],
'vif_type': PORT_TO_PROCESS['binding:vif_type']})
smartnic_data = {
'mac': mac,
'vm_uuid': vm_uuid,
'iface_name': rep_port,
'iface_id': iface_id,
'vif_type': vif_type}
self.assertEqual([smartnic_data],
self.agent.updated_smartnic_ports)
def test_add_port_to_updated_smartnic_ports_port_as_object(self):
vm_uuid = "407a79e0-e0be-4b7d-92a6-513b2161011b"
iface_id = "407a79e0-e0be-4b7d-92a6-513b2161011c"
rep_port = 'rep0-0'
mac = FAKE_MAC
vif_type = "ovs"
port = Port()
port['id'] = iface_id
port['mac_address'] = netaddr.EUI(mac)
port['device_id'] = vm_uuid
port_bind = PortBinding()
port_bind['profile'] = {'local_link_information': [
{'hostname': 'host1', 'port_id': rep_port}]}
port_bind['vif_type'] = vif_type
port_bind['host'] = 'host'
port_bind['vnic_type'] = portbindings.VNIC_SMARTNIC
port.bindings = [port_bind]
self.agent._add_port_to_updated_smartnic_ports(port, port_bind)
smartnic_data = {
'mac': mac,
'vm_uuid': vm_uuid,
'iface_name': rep_port,
'iface_id': iface_id,
'vif_type': vif_type}
self.assertEqual([smartnic_data],
self.agent.updated_smartnic_ports)
class TestOvsNeutronAgentOFCtl(TestOvsNeutronAgent,
ovs_test_base.OVSOFCtlTestBase):
@ -3152,7 +3334,10 @@ class TestOvsDvrNeutronAgent(object):
mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\
mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\
mock.patch.dict(self.agent.dvr_agent.phys_brs,
{self._physical_network: phys_br}):
{self._physical_network: phys_br}),\
mock.patch.object(self.agent.int_br,
'get_vif_port_by_id',
return_value=None):
failed_devices = {'added': set(), 'removed': set()}
failed_devices['removed'] = self.agent.treat_devices_removed(
[self._port.vif_id])
@ -3370,7 +3555,9 @@ class TestOvsDvrNeutronAgent(object):
mock.patch.object(self.agent, 'int_br', new=int_br),\
mock.patch.object(self.agent, 'tun_br', new=tun_br),\
mock.patch.object(self.agent.dvr_agent, 'int_br', new=int_br),\
mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br):
mock.patch.object(self.agent.dvr_agent, 'tun_br', new=tun_br),\
mock.patch.object(self.agent.int_br, 'get_vif_port_by_id',
return_value=None):
failed_devices = {'added': set(), 'removed': set()}
failed_devices['removed'] = self.agent.treat_devices_removed(
[self._port.vif_id])

View File

@ -320,19 +320,22 @@ class OpenvswitchMechanismSRIOVTestCase(OpenvswitchMechanismBaseTestCase):
class OpenvswitchMechVnicTypesTestCase(OpenvswitchMechanismBaseTestCase):
supported_vnics = [portbindings.VNIC_NORMAL,
portbindings.VNIC_DIRECT,
portbindings.VNIC_SMARTNIC]
def setUp(self):
self.blacklist_cfg = {
'OVS_DRIVER': {
'vnic_type_blacklist': []
}
}
self.default_supported_vnics = [portbindings.VNIC_NORMAL,
portbindings.VNIC_DIRECT]
self.default_supported_vnics = self.supported_vnics
super(OpenvswitchMechVnicTypesTestCase, self).setUp()
def test_default_vnic_types(self):
self.assertEqual([portbindings.VNIC_NORMAL,
portbindings.VNIC_DIRECT],
self.assertEqual(self.default_supported_vnics,
self.driver.supported_vnic_types)
def test_vnic_type_blacklist_valid_item(self):
@ -366,7 +369,7 @@ class OpenvswitchMechVnicTypesTestCase(OpenvswitchMechanismBaseTestCase):
def test_vnic_type_blacklist_all_items(self):
self.blacklist_cfg['OVS_DRIVER']['vnic_type_blacklist'] = \
[portbindings.VNIC_NORMAL, portbindings.VNIC_DIRECT]
self.supported_vnics
fake_conf = cfg.CONF
fake_conf_fixture = base.MechDriverConfFixture(
fake_conf, self.blacklist_cfg,

View File

@ -494,3 +494,12 @@ class RpcApiTestCase(base.BaseTestCase):
devices=['fake_device1', 'fake_device2'],
agent_id='fake_agent_id', host='fake_host',
version='1.5')
def test_get_ports_by_vnic_type_and_host(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
self._test_rpc_api(rpcapi, None,
'get_ports_by_vnic_type_and_host',
rpc_method='call',
vnic_type='fake_device1',
host='fake_host',
version='1.7')

View File

@ -0,0 +1,7 @@
---
prelude: >
Add Support for Smart NIC in ML2/OVS mechanism driver.
features:
- |
Add Support for Smart NIC in ML2/OVS mechanism driver, by extending the Neutron OVS mechanism
driver and Neutron OVS Agent to bind the Neutron port for the baremetal host with Smart NIC.

View File

@ -53,3 +53,4 @@ weakrefmethod>=1.0.2;python_version=='2.7' # PSF
python-novaclient>=9.1.0 # Apache-2.0
python-designateclient>=2.7.0 # Apache-2.0
os-xenapi>=0.3.1 # Apache-2.0
os-vif>=1.15.1 # Apache-2.0