Merge "Add network_update RPC into SR-IOV agent"
This commit is contained in:
commit
8ea3b36a44
@ -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)
|
||||||
|
@ -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):
|
||||||
|
Loading…
Reference in New Issue
Block a user