Merge "port-hint-ovs-tx-steering: agent side"
This commit is contained in:
commit
de1a3a84b6
@ -384,6 +384,7 @@ class CacheBackedPluginApi(PluginApi):
|
||||
'vnic_type': binding.vnic_type,
|
||||
'security_groups': list(port_obj.security_group_ids),
|
||||
'migrating_to': migrating_to,
|
||||
'hints': port_obj.hints.hints if port_obj.hints else None,
|
||||
}
|
||||
LOG.debug("Returning: %s", entry)
|
||||
return entry
|
||||
|
@ -83,6 +83,10 @@ cfg.CONF.import_group('OVS', 'neutron.plugins.ml2.drivers.openvswitch.agent.'
|
||||
|
||||
INIT_MAX_TRIES = 3
|
||||
|
||||
PORT_HINTS_TX_STEERING = 'tx-steering'
|
||||
PORT_HINTS_TX_STEERING_HASH = 'hash'
|
||||
PORT_HINTS_TX_STEERING_THREAD = 'thread'
|
||||
|
||||
|
||||
class _mac_mydialect(netaddr.mac_unix):
|
||||
word_fmt = '%.2x'
|
||||
@ -1250,6 +1254,28 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||
if self.prevent_arp_spoofing:
|
||||
self.setup_arp_spoofing_protection(self.int_br,
|
||||
port, port_detail)
|
||||
# Apply port hints
|
||||
try:
|
||||
to_set = self.sanitize_ovs_iface_other_config(
|
||||
port_detail['hints']['openvswitch']['other_config'])
|
||||
self.int_br.set_db_attribute(
|
||||
'Interface', port.port_name, 'other_config', to_set)
|
||||
# Clear the config from ovs when we receive:
|
||||
# * an empty hints attribute
|
||||
# * an invalid hints value, for example:
|
||||
# * invalid key: KeyError
|
||||
# * anything that's not a dict in hints, like None: TypeError
|
||||
except (KeyError, TypeError):
|
||||
self.int_br.clear_db_attribute(
|
||||
'Interface', port.port_name, 'other_config')
|
||||
finally:
|
||||
LOG.debug(
|
||||
'port-hint-ovs-tx-steering: vif=%s other_config=%s',
|
||||
port.port_name,
|
||||
self.int_br.db_get_val(
|
||||
'Interface', port.port_name, 'other_config')
|
||||
)
|
||||
|
||||
if cur_tag != lvm.vlan:
|
||||
ovsdb = self.int_br.ovsdb
|
||||
with ovsdb.transaction() as txn:
|
||||
@ -1302,6 +1328,27 @@ class OVSNeutronAgent(l2population_rpc.L2populationRpcCallBackTunnelMixin,
|
||||
{'up': devices_up, 'down': devices_down})
|
||||
return set(failed_devices)
|
||||
|
||||
@staticmethod
|
||||
def sanitize_ovs_iface_other_config(other_config):
|
||||
'''Take an other_config dict meant for an ovs interface.
|
||||
|
||||
Return a filtered/sanitized version of it with only keys and values
|
||||
we consider accepted. Log if the output differs from the input.
|
||||
'''
|
||||
output = {}
|
||||
if PORT_HINTS_TX_STEERING in other_config:
|
||||
if other_config[PORT_HINTS_TX_STEERING] in (
|
||||
PORT_HINTS_TX_STEERING_HASH,
|
||||
PORT_HINTS_TX_STEERING_THREAD):
|
||||
output[PORT_HINTS_TX_STEERING] = other_config[
|
||||
PORT_HINTS_TX_STEERING]
|
||||
if other_config != output:
|
||||
LOG.warning(
|
||||
'Got unexpected other_config, using sanitized version!'
|
||||
' got=%s sanitized=%s', other_config, output,
|
||||
)
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
def setup_arp_spoofing_protection(bridge, vif, port_details):
|
||||
if not port_details.get('port_security_enabled', True):
|
||||
|
@ -29,6 +29,7 @@ from oslo_utils import uuidutils
|
||||
|
||||
from neutron.agent import rpc
|
||||
from neutron.objects import network
|
||||
from neutron.objects.port.extensions import port_hints
|
||||
from neutron.objects import ports
|
||||
from neutron.tests import base
|
||||
|
||||
@ -221,7 +222,10 @@ class TestCacheBackedPluginApi(base.BaseTestCase):
|
||||
host='host1',
|
||||
level=0,
|
||||
segment=self._segment)],
|
||||
status='ACTIVE')
|
||||
status='ACTIVE',
|
||||
hints=port_hints.PortHints(hints={
|
||||
"openvswitch": {"other_config": {"tx-steering": "hash"}}}),
|
||||
)
|
||||
|
||||
def test__legacy_notifier_resource_delete(self):
|
||||
self._api._legacy_notifier(resources.PORT, events.AFTER_DELETE, self,
|
||||
@ -360,6 +364,15 @@ class TestCacheBackedPluginApi(base.BaseTestCase):
|
||||
mock.ANY, 'host2')
|
||||
self.assertEqual('host2', entry['migrating_to'])
|
||||
|
||||
def test_get_device_details_hints(self):
|
||||
self._api.remote_resource_cache.get_resource_by_id.side_effect = [
|
||||
self._port, self._network]
|
||||
entry = self._api.get_device_details(
|
||||
mock.ANY, self._port_id, mock.ANY, mock.ANY)
|
||||
self.assertEqual(
|
||||
{"openvswitch": {"other_config": {"tx-steering": "hash"}}},
|
||||
entry['hints'])
|
||||
|
||||
@mock.patch('neutron.agent.resource_cache.RemoteResourceCache')
|
||||
def test_initialization_with_default_resources(self, rcache_class):
|
||||
rcache_obj = mock.MagicMock()
|
||||
|
@ -738,6 +738,78 @@ class TestOvsNeutronAgent(object):
|
||||
mock.ANY, mock.ANY,
|
||||
refresh_tunnels=True)
|
||||
|
||||
def test_bind_devices_hints_valid_hints(self):
|
||||
self.agent.vlan_manager.mapping['net1']['seg1'] = mock.Mock()
|
||||
ovs_db_list = [{'name': 'tap1', 'tag': []}]
|
||||
vif_port1 = mock.Mock()
|
||||
vif_port1.port_name = 'tap1'
|
||||
port_details = [
|
||||
{'network_id': 'net1',
|
||||
'vif_port': vif_port1,
|
||||
'segmentation_id': 'seg1',
|
||||
'device': 'tap1',
|
||||
'device_owner': 'network:dhcp',
|
||||
'admin_state_up': True,
|
||||
'hints': {
|
||||
'openvswitch': {'other_config': {'tx-steering': 'hash'}}}},
|
||||
]
|
||||
with mock.patch.object(self.agent.plugin_rpc, 'update_device_list'), \
|
||||
mock.patch.object(self.agent, 'int_br') as mock_int_br:
|
||||
mock_int_br.get_ports_attributes.return_value = ovs_db_list
|
||||
self.agent._bind_devices(port_details)
|
||||
mock_int_br.set_db_attribute.assert_called_once_with(
|
||||
'Interface',
|
||||
vif_port1.port_name,
|
||||
'other_config',
|
||||
{'tx-steering': 'hash'})
|
||||
|
||||
def test_bind_devices_hints_no_hints(self):
|
||||
self.agent.vlan_manager.mapping['net1']['seg1'] = mock.Mock()
|
||||
ovs_db_list = [{'name': 'tap1', 'tag': []}]
|
||||
vif_port1 = mock.Mock()
|
||||
vif_port1.port_name = 'tap1'
|
||||
port_details = [
|
||||
{'network_id': 'net1',
|
||||
'vif_port': vif_port1,
|
||||
'segmentation_id': 'seg1',
|
||||
'device': 'tap1',
|
||||
'device_owner': 'network:dhcp',
|
||||
'admin_state_up': True,
|
||||
'hints': {}},
|
||||
]
|
||||
with mock.patch.object(self.agent.plugin_rpc, 'update_device_list'), \
|
||||
mock.patch.object(self.agent, 'int_br') as mock_int_br:
|
||||
mock_int_br.get_ports_attributes.return_value = ovs_db_list
|
||||
self.agent._bind_devices(port_details)
|
||||
mock_int_br.clear_db_attribute.assert_called_once_with(
|
||||
'Interface',
|
||||
vif_port1.port_name,
|
||||
'other_config')
|
||||
|
||||
def test_bind_devices_hints_invalid_hints(self):
|
||||
self.agent.vlan_manager.mapping['net1']['seg1'] = mock.Mock()
|
||||
ovs_db_list = [{'name': 'tap1', 'tag': []}]
|
||||
vif_port1 = mock.Mock()
|
||||
vif_port1.port_name = 'tap1'
|
||||
port_details = [
|
||||
{'network_id': 'net1',
|
||||
'vif_port': vif_port1,
|
||||
'segmentation_id': 'seg1',
|
||||
'device': 'tap1',
|
||||
'device_owner': 'network:dhcp',
|
||||
'admin_state_up': True,
|
||||
'hints': {
|
||||
'openvswitch': {'not-a-valid-key': {'tx-steering': 'hash'}}}},
|
||||
]
|
||||
with mock.patch.object(self.agent.plugin_rpc, 'update_device_list'), \
|
||||
mock.patch.object(self.agent, 'int_br') as mock_int_br:
|
||||
mock_int_br.get_ports_attributes.return_value = ovs_db_list
|
||||
self.agent._bind_devices(port_details)
|
||||
mock_int_br.clear_db_attribute.assert_called_once_with(
|
||||
'Interface',
|
||||
vif_port1.port_name,
|
||||
'other_config')
|
||||
|
||||
def _test_bind_devices_sets_refresh_tunnels(self, tun_ofports, expected):
|
||||
self.agent.iter_num = 3
|
||||
self.agent.prevent_arp_spoofing = False
|
||||
@ -2910,6 +2982,32 @@ class TestOvsNeutronAgent(object):
|
||||
self.agent.create_smartnic_port_map_entry_data(mac, rep_port)
|
||||
self.assertEqual(int_br_smartnic_port_map, expected_return_value)
|
||||
|
||||
def test_sanitize_ovs_iface_other_config(self):
|
||||
self.assertEqual(
|
||||
{},
|
||||
self.agent.sanitize_ovs_iface_other_config({}),
|
||||
)
|
||||
self.assertEqual(
|
||||
{"tx-steering": "hash"},
|
||||
self.agent.sanitize_ovs_iface_other_config(
|
||||
{"tx-steering": "hash"}),
|
||||
)
|
||||
self.assertEqual(
|
||||
{"tx-steering": "thread"},
|
||||
self.agent.sanitize_ovs_iface_other_config(
|
||||
{"tx-steering": "thread"}),
|
||||
)
|
||||
self.assertEqual(
|
||||
{},
|
||||
self.agent.sanitize_ovs_iface_other_config(
|
||||
{"tx-steering": "invalid"}),
|
||||
)
|
||||
self.assertEqual(
|
||||
{},
|
||||
self.agent.sanitize_ovs_iface_other_config(
|
||||
{"invalid": "thread"}),
|
||||
)
|
||||
|
||||
|
||||
class TestOvsNeutronAgentOSKen(TestOvsNeutronAgent,
|
||||
ovs_test_base.OVSOSKenTestBase):
|
||||
|
Loading…
x
Reference in New Issue
Block a user