Merge "Add Smart NIC representor port to integration bridge"
This commit is contained in:
commit
3e6376f428
@ -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)
|
||||
|
@ -162,7 +162,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 TestOvsNeutronAgentOSKen(TestOvsNeutronAgent,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
@ -3124,7 +3306,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])
|
||||
@ -3342,7 +3527,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])
|
||||
|
@ -329,19 +329,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):
|
||||
@ -375,7 +378,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…
Reference in New Issue
Block a user