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:
parent
0c87350a94
commit
b4243ad3f7
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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.")),
|
||||
]
|
||||
|
||||
|
||||
|
@ -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()]
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
@ -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]
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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')))
|
||||
|
@ -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])
|
||||
|
@ -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,
|
||||
|
@ -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')
|
||||
|
@ -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.
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user