Merge "Add network_update RPC into SR-IOV agent"
This commit is contained in:
commit
8ea3b36a44
@ -14,6 +14,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
import collections
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
@ -22,6 +23,7 @@ from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import oslo_messaging
|
||||
from oslo_service import loopingcall
|
||||
import six
|
||||
|
||||
from neutron._i18n import _, _LE, _LI, _LW
|
||||
from neutron.agent.l2.extensions import manager as ext_manager
|
||||
@ -46,8 +48,13 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
||||
|
||||
# Set RPC API version to 1.0 by default.
|
||||
# history
|
||||
# 1.1 Support Security Group RPC
|
||||
target = oslo_messaging.Target(version='1.1')
|
||||
# 1.1 Support Security Group RPC (works with NoopFirewallDriver)
|
||||
# 1.2 Support DVR (Distributed Virtual Router) RPC (not supported)
|
||||
# 1.3 Added param devices_to_update to security_groups_provider_updated
|
||||
# (works with NoopFirewallDriver)
|
||||
# 1.4 Added support for network_update
|
||||
|
||||
target = oslo_messaging.Target(version='1.4')
|
||||
|
||||
def __init__(self, context, agent, sg_agent):
|
||||
super(SriovNicSwitchRpcCallbacks, self).__init__()
|
||||
@ -84,19 +91,28 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
|
||||
"skipping", {'id': port['id'], 'mac': mac,
|
||||
'pci_slot': pci_slot})
|
||||
|
||||
def network_update(self, context, **kwargs):
|
||||
network_id = kwargs['network']['id']
|
||||
LOG.debug("network_update message received for network "
|
||||
"%(network_id)s, with ports: %(ports)s",
|
||||
{'network_id': network_id,
|
||||
'ports': self.agent.network_ports[network_id]})
|
||||
for port_data in self.agent.network_ports[network_id]:
|
||||
self.agent.updated_devices.add(port_data['device'])
|
||||
|
||||
|
||||
class SriovNicSwitchAgent(object):
|
||||
def __init__(self, physical_devices_mappings, exclude_devices,
|
||||
polling_interval):
|
||||
|
||||
self.polling_interval = polling_interval
|
||||
self.network_ports = collections.defaultdict(list)
|
||||
self.conf = cfg.CONF
|
||||
self.setup_eswitch_mgr(physical_devices_mappings,
|
||||
exclude_devices)
|
||||
|
||||
# Stores port update notifications for processing in the main loop
|
||||
self.updated_devices = set()
|
||||
self.mac_to_port_id_mapping = {}
|
||||
|
||||
self.context = context.get_admin_context_without_session()
|
||||
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
|
||||
@ -135,6 +151,7 @@ class SriovNicSwitchAgent(object):
|
||||
# Define the listening consumers for the agent
|
||||
consumers = [[topics.PORT, topics.UPDATE],
|
||||
[topics.NETWORK, topics.DELETE],
|
||||
[topics.NETWORK, topics.UPDATE],
|
||||
[topics.SECURITY_GROUP, topics.UPDATE]]
|
||||
self.connection = agent_rpc.create_consumers(self.endpoints,
|
||||
self.topic,
|
||||
@ -173,10 +190,11 @@ class SriovNicSwitchAgent(object):
|
||||
device_info = {}
|
||||
device_info['current'] = curr_devices
|
||||
device_info['added'] = curr_devices - registered_devices
|
||||
# we don't want to process updates for devices that don't exist
|
||||
device_info['updated'] = updated_devices & curr_devices
|
||||
# we need to clean up after devices are removed
|
||||
device_info['removed'] = registered_devices - curr_devices
|
||||
# we don't want to process updates for devices that don't exist
|
||||
device_info['updated'] = (updated_devices & curr_devices -
|
||||
device_info['removed'])
|
||||
return device_info
|
||||
|
||||
def _device_info_has_changes(self, device_info):
|
||||
@ -239,6 +257,21 @@ class SriovNicSwitchAgent(object):
|
||||
else:
|
||||
LOG.info(_LI("No device with MAC %s defined on agent."), device)
|
||||
|
||||
def _update_network_ports(self, network_id, port_id, mac_pci_slot):
|
||||
self._clean_network_ports(mac_pci_slot)
|
||||
self.network_ports[network_id].append({
|
||||
"port_id": port_id,
|
||||
"device": mac_pci_slot})
|
||||
|
||||
def _clean_network_ports(self, mac_pci_slot):
|
||||
for netid, ports_list in six.iteritems(self.network_ports):
|
||||
for port_data in ports_list:
|
||||
if mac_pci_slot == port_data['device']:
|
||||
ports_list.remove(port_data)
|
||||
if ports_list == []:
|
||||
self.network_ports.pop(netid)
|
||||
return port_data['port_id']
|
||||
|
||||
def treat_devices_added_updated(self, devices_info):
|
||||
try:
|
||||
macs_list = set([device_info[0] for device_info in devices_info])
|
||||
@ -259,13 +292,15 @@ class SriovNicSwitchAgent(object):
|
||||
LOG.info(_LI("Port %(device)s updated. Details: %(details)s"),
|
||||
{'device': device, 'details': device_details})
|
||||
port_id = device_details['port_id']
|
||||
self.mac_to_port_id_mapping[device] = port_id
|
||||
profile = device_details['profile']
|
||||
spoofcheck = device_details.get('port_security_enabled', True)
|
||||
self.treat_device(device,
|
||||
profile.get('pci_slot'),
|
||||
device_details['admin_state_up'],
|
||||
spoofcheck)
|
||||
self._update_network_ports(device_details['network_id'],
|
||||
port_id,
|
||||
(device, profile.get('pci_slot')))
|
||||
self.ext_manager.handle_port(self.context, device_details)
|
||||
else:
|
||||
LOG.info(_LI("Device with MAC %s not defined on plugin"),
|
||||
@ -280,14 +315,12 @@ class SriovNicSwitchAgent(object):
|
||||
"PCI slot %(pci_slot)s"),
|
||||
{'mac': mac, 'pci_slot': pci_slot})
|
||||
try:
|
||||
port_id = self.mac_to_port_id_mapping.get(mac)
|
||||
port_id = self._clean_network_ports(device)
|
||||
if port_id:
|
||||
profile = {'pci_slot': pci_slot}
|
||||
port = {'port_id': port_id,
|
||||
'device': mac,
|
||||
'profile': profile}
|
||||
'profile': {'pci_slot': pci_slot}}
|
||||
self.ext_manager.delete_port(self.context, port)
|
||||
del self.mac_to_port_id_mapping[mac]
|
||||
else:
|
||||
LOG.warning(_LW("port_id to device with MAC "
|
||||
"%s not found"), mac)
|
||||
|
@ -150,6 +150,17 @@ class TestSriovAgent(base.BaseTestCase):
|
||||
'removed': set(['1'])}
|
||||
self.mock_scan_devices(expected, mock_current, registered, updated)
|
||||
|
||||
def test_scan_devices_updated_and_removed(self):
|
||||
registered = set(['1', '2'])
|
||||
# '1' is in removed and updated tuple
|
||||
updated = set(['1'])
|
||||
mock_current = set(['2', '3'])
|
||||
expected = {'current': set(['2', '3']),
|
||||
'updated': set(),
|
||||
'added': set(['3']),
|
||||
'removed': set(['1'])}
|
||||
self.mock_scan_devices(expected, mock_current, registered, updated)
|
||||
|
||||
def test_scan_devices_new_updates(self):
|
||||
registered = set(['1'])
|
||||
updated = set(['2'])
|
||||
@ -191,6 +202,56 @@ class TestSriovAgent(base.BaseTestCase):
|
||||
'mac4']))
|
||||
agent.treat_devices_removed.assert_called_with(set(['mac1']))
|
||||
|
||||
def test_treat_devices_added_updated_and_removed(self):
|
||||
agent = self.agent
|
||||
MAC1 = 'aa:bb:cc:dd:ee:ff'
|
||||
SLOT1 = '1:2:3.0'
|
||||
MAC2 = 'aa:bb:cc:dd:ee:fe'
|
||||
SLOT2 = '1:3:3.0'
|
||||
mac_pci_slot_device1 = (MAC1, SLOT1)
|
||||
mac_pci_slot_device2 = (MAC2, SLOT2)
|
||||
mock_device1_details = {'device': MAC1,
|
||||
'port_id': 'port123',
|
||||
'network_id': 'net123',
|
||||
'admin_state_up': True,
|
||||
'network_type': 'vlan',
|
||||
'segmentation_id': 100,
|
||||
'profile': {'pci_slot': SLOT1},
|
||||
'physical_network': 'physnet1',
|
||||
'port_security_enabled': False}
|
||||
mock_device2_details = {'device': MAC2,
|
||||
'port_id': 'port124',
|
||||
'network_id': 'net123',
|
||||
'admin_state_up': True,
|
||||
'network_type': 'vlan',
|
||||
'segmentation_id': 100,
|
||||
'profile': {'pci_slot': SLOT2},
|
||||
'physical_network': 'physnet1',
|
||||
'port_security_enabled': False}
|
||||
agent.plugin_rpc = mock.Mock()
|
||||
agent.plugin_rpc.get_devices_details_list.return_value = (
|
||||
[mock_device1_details])
|
||||
agent.treat_devices_added_updated(set([MAC1]))
|
||||
self.assertEqual({'net123': [{'port_id': 'port123',
|
||||
'device': mac_pci_slot_device1}]},
|
||||
agent.network_ports)
|
||||
agent.plugin_rpc.get_devices_details_list.return_value = (
|
||||
[mock_device2_details])
|
||||
# add the second device and check the network_ports dict
|
||||
agent.treat_devices_added_updated(set([MAC2]))
|
||||
self.assertEqual(
|
||||
{'net123': [{'port_id': 'port123',
|
||||
'device': mac_pci_slot_device1}, {'port_id': 'port124',
|
||||
'device': mac_pci_slot_device2}]},
|
||||
agent.network_ports)
|
||||
with mock.patch.object(agent.plugin_rpc,
|
||||
"update_device_down"):
|
||||
agent.treat_devices_removed([mac_pci_slot_device2])
|
||||
# remove the second device and check the network_ports dict
|
||||
self.assertEqual({'net123': [{'port_id': 'port123',
|
||||
'device': mac_pci_slot_device1}]},
|
||||
agent.network_ports)
|
||||
|
||||
def test_treat_devices_added_updated_admin_state_up_true(self):
|
||||
agent = self.agent
|
||||
mock_details = {'device': 'aa:bb:cc:dd:ee:ff',
|
||||
@ -268,6 +329,35 @@ class TestSriovAgent(base.BaseTestCase):
|
||||
self.assertFalse(resync_needed)
|
||||
self.assertFalse(agent.plugin_rpc.update_device_up.called)
|
||||
|
||||
def test_update_and_clean_network_ports(self):
|
||||
network_id1 = 'network_id1'
|
||||
network_id2 = 'network_id2'
|
||||
|
||||
port_id1 = 'port_id1'
|
||||
port_id2 = 'port_id2'
|
||||
mac_slot_1 = ('mac1', 'slot1')
|
||||
mac_slot_2 = ('mac2', 'slot2')
|
||||
|
||||
self.agent.network_ports[network_id1] = [{'port_id': port_id1,
|
||||
'device': mac_slot_1}, {'port_id': port_id2, 'device': mac_slot_2}]
|
||||
|
||||
self.agent._update_network_ports(network_id2, port_id1, mac_slot_1)
|
||||
|
||||
self.assertEqual({network_id1: [{'port_id': port_id2,
|
||||
'device': mac_slot_2}], network_id2: [
|
||||
{'port_id': port_id1, 'device': mac_slot_1}]},
|
||||
self.agent.network_ports)
|
||||
|
||||
cleaned_port_id = self.agent._clean_network_ports(mac_slot_1)
|
||||
self.assertEqual(cleaned_port_id, port_id1)
|
||||
|
||||
self.assertEqual({network_id1: [{'port_id': port_id2,
|
||||
'device': mac_slot_2}]},
|
||||
self.agent.network_ports)
|
||||
|
||||
cleaned_port_id = self.agent._clean_network_ports(mac_slot_2)
|
||||
self.assertEqual({}, self.agent.network_ports)
|
||||
|
||||
|
||||
class FakeAgent(object):
|
||||
def __init__(self):
|
||||
@ -310,6 +400,23 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
|
||||
self.sriov_rpc_callback.port_update(**kwargs)
|
||||
self.assertEqual(set(), self.agent.updated_devices)
|
||||
|
||||
def test_network_update(self):
|
||||
TEST_NETWORK_ID1 = "n1"
|
||||
TEST_NETWORK_ID2 = "n2"
|
||||
TEST_PORT_ID1 = 'p1'
|
||||
TEST_PORT_ID2 = 'p2'
|
||||
network1 = {'id': TEST_NETWORK_ID1}
|
||||
port1 = {'id': TEST_PORT_ID1, 'network_id': TEST_NETWORK_ID1}
|
||||
port2 = {'id': TEST_PORT_ID2, 'network_id': TEST_NETWORK_ID2}
|
||||
self.agent.network_ports = {
|
||||
TEST_NETWORK_ID1: [{'port_id': port1['id'],
|
||||
'device': ('mac1', 'slot1')}],
|
||||
TEST_NETWORK_ID2: [{'port_id': port2['id'],
|
||||
'device': ('mac2', 'slot2')}]}
|
||||
kwargs = {'context': self.context, 'network': network1}
|
||||
self.sriov_rpc_callback.network_update(**kwargs)
|
||||
self.assertEqual(set([('mac1', 'slot1')]), self.agent.updated_devices)
|
||||
|
||||
|
||||
class TestSRIOVAgentExtensionConfig(base.BaseTestCase):
|
||||
def setUp(self):
|
||||
|
Loading…
Reference in New Issue
Block a user