Merge "Add network_update RPC into SR-IOV agent"

This commit is contained in:
Jenkins 2016-01-27 12:47:20 +00:00 committed by Gerrit Code Review
commit 8ea3b36a44
2 changed files with 150 additions and 10 deletions

View File

@ -14,6 +14,7 @@
# limitations under the License. # limitations under the License.
import collections
import socket import socket
import sys import sys
import time import time
@ -22,6 +23,7 @@ from oslo_config import cfg
from oslo_log import log as logging from oslo_log import log as logging
import oslo_messaging import oslo_messaging
from oslo_service import loopingcall from oslo_service import loopingcall
import six
from neutron._i18n import _, _LE, _LI, _LW from neutron._i18n import _, _LE, _LI, _LW
from neutron.agent.l2.extensions import manager as ext_manager 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. # Set RPC API version to 1.0 by default.
# history # history
# 1.1 Support Security Group RPC # 1.1 Support Security Group RPC (works with NoopFirewallDriver)
target = oslo_messaging.Target(version='1.1') # 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): def __init__(self, context, agent, sg_agent):
super(SriovNicSwitchRpcCallbacks, self).__init__() super(SriovNicSwitchRpcCallbacks, self).__init__()
@ -84,19 +91,28 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
"skipping", {'id': port['id'], 'mac': mac, "skipping", {'id': port['id'], 'mac': mac,
'pci_slot': pci_slot}) '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): class SriovNicSwitchAgent(object):
def __init__(self, physical_devices_mappings, exclude_devices, def __init__(self, physical_devices_mappings, exclude_devices,
polling_interval): polling_interval):
self.polling_interval = polling_interval self.polling_interval = polling_interval
self.network_ports = collections.defaultdict(list)
self.conf = cfg.CONF self.conf = cfg.CONF
self.setup_eswitch_mgr(physical_devices_mappings, self.setup_eswitch_mgr(physical_devices_mappings,
exclude_devices) exclude_devices)
# Stores port update notifications for processing in the main loop # Stores port update notifications for processing in the main loop
self.updated_devices = set() self.updated_devices = set()
self.mac_to_port_id_mapping = {}
self.context = context.get_admin_context_without_session() self.context = context.get_admin_context_without_session()
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN) self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
@ -135,6 +151,7 @@ class SriovNicSwitchAgent(object):
# Define the listening consumers for the agent # Define the listening consumers for the agent
consumers = [[topics.PORT, topics.UPDATE], consumers = [[topics.PORT, topics.UPDATE],
[topics.NETWORK, topics.DELETE], [topics.NETWORK, topics.DELETE],
[topics.NETWORK, topics.UPDATE],
[topics.SECURITY_GROUP, topics.UPDATE]] [topics.SECURITY_GROUP, topics.UPDATE]]
self.connection = agent_rpc.create_consumers(self.endpoints, self.connection = agent_rpc.create_consumers(self.endpoints,
self.topic, self.topic,
@ -173,10 +190,11 @@ class SriovNicSwitchAgent(object):
device_info = {} device_info = {}
device_info['current'] = curr_devices device_info['current'] = curr_devices
device_info['added'] = curr_devices - registered_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 # we need to clean up after devices are removed
device_info['removed'] = registered_devices - curr_devices 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 return device_info
def _device_info_has_changes(self, device_info): def _device_info_has_changes(self, device_info):
@ -239,6 +257,21 @@ class SriovNicSwitchAgent(object):
else: else:
LOG.info(_LI("No device with MAC %s defined on agent."), device) 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): def treat_devices_added_updated(self, devices_info):
try: try:
macs_list = set([device_info[0] for device_info in devices_info]) 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"), LOG.info(_LI("Port %(device)s updated. Details: %(details)s"),
{'device': device, 'details': device_details}) {'device': device, 'details': device_details})
port_id = device_details['port_id'] port_id = device_details['port_id']
self.mac_to_port_id_mapping[device] = port_id
profile = device_details['profile'] profile = device_details['profile']
spoofcheck = device_details.get('port_security_enabled', True) spoofcheck = device_details.get('port_security_enabled', True)
self.treat_device(device, self.treat_device(device,
profile.get('pci_slot'), profile.get('pci_slot'),
device_details['admin_state_up'], device_details['admin_state_up'],
spoofcheck) 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) self.ext_manager.handle_port(self.context, device_details)
else: else:
LOG.info(_LI("Device with MAC %s not defined on plugin"), LOG.info(_LI("Device with MAC %s not defined on plugin"),
@ -280,14 +315,12 @@ class SriovNicSwitchAgent(object):
"PCI slot %(pci_slot)s"), "PCI slot %(pci_slot)s"),
{'mac': mac, 'pci_slot': pci_slot}) {'mac': mac, 'pci_slot': pci_slot})
try: try:
port_id = self.mac_to_port_id_mapping.get(mac) port_id = self._clean_network_ports(device)
if port_id: if port_id:
profile = {'pci_slot': pci_slot}
port = {'port_id': port_id, port = {'port_id': port_id,
'device': mac, 'device': mac,
'profile': profile} 'profile': {'pci_slot': pci_slot}}
self.ext_manager.delete_port(self.context, port) self.ext_manager.delete_port(self.context, port)
del self.mac_to_port_id_mapping[mac]
else: else:
LOG.warning(_LW("port_id to device with MAC " LOG.warning(_LW("port_id to device with MAC "
"%s not found"), mac) "%s not found"), mac)

View File

@ -150,6 +150,17 @@ class TestSriovAgent(base.BaseTestCase):
'removed': set(['1'])} 'removed': set(['1'])}
self.mock_scan_devices(expected, mock_current, registered, updated) 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): def test_scan_devices_new_updates(self):
registered = set(['1']) registered = set(['1'])
updated = set(['2']) updated = set(['2'])
@ -191,6 +202,56 @@ class TestSriovAgent(base.BaseTestCase):
'mac4'])) 'mac4']))
agent.treat_devices_removed.assert_called_with(set(['mac1'])) 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): def test_treat_devices_added_updated_admin_state_up_true(self):
agent = self.agent agent = self.agent
mock_details = {'device': 'aa:bb:cc:dd:ee:ff', mock_details = {'device': 'aa:bb:cc:dd:ee:ff',
@ -268,6 +329,35 @@ class TestSriovAgent(base.BaseTestCase):
self.assertFalse(resync_needed) self.assertFalse(resync_needed)
self.assertFalse(agent.plugin_rpc.update_device_up.called) 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): class FakeAgent(object):
def __init__(self): def __init__(self):
@ -310,6 +400,23 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
self.sriov_rpc_callback.port_update(**kwargs) self.sriov_rpc_callback.port_update(**kwargs)
self.assertEqual(set(), self.agent.updated_devices) 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): class TestSRIOVAgentExtensionConfig(base.BaseTestCase):
def setUp(self): def setUp(self):