Merge "SR-IOV agent can handle ports with same MAC addresses"

This commit is contained in:
Zuul 2021-06-22 19:21:53 +00:00 committed by Gerrit Code Review
commit 4b0a225e80
13 changed files with 344 additions and 398 deletions

View File

@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
import collections
from datetime import datetime
import itertools
@ -38,6 +39,7 @@ from neutron import objects
LOG = logging.getLogger(__name__)
BINDING_DEACTIVATE = 'binding_deactivate'
DeviceInfo = collections.namedtuple('DeviceInfo', 'mac pci_slot')
def create_consumers(endpoints, prefix, topic_details, start_listening=True):
@ -118,19 +120,26 @@ class PluginApi(object):
1.7 - Support get_ports_by_vnic_type_and_host
1.8 - Rename agent_restarted to refresh_tunnels in
update_device_list to reflect its expanded purpose
1.9 - Support for device definition as DeviceInfo(mac, pci_info) for:
- get_device_details
- get_devices_details_list (indirectly, calls get_device_details)
- update_device_down
- update_device_up
- update_device_list (indirectly, called from update_device_down
and update_device_up)
'''
def __init__(self, topic):
target = oslo_messaging.Target(topic=topic, version='1.0')
target = oslo_messaging.Target(topic=topic, version='1.9')
self.client = lib_rpc.get_client(target)
def get_device_details(self, context, device, agent_id, host=None):
cctxt = self.client.prepare()
cctxt = self.client.prepare(version='1.9')
return cctxt.call(context, 'get_device_details', device=device,
agent_id=agent_id, host=host)
def get_devices_details_list(self, context, devices, agent_id, host=None):
cctxt = self.client.prepare(version='1.3')
cctxt = self.client.prepare(version='1.9')
return cctxt.call(context, 'get_devices_details_list',
devices=devices, agent_id=agent_id, host=host)
@ -155,18 +164,18 @@ class PluginApi(object):
agent_id=agent_id, host=host)
def update_device_down(self, context, device, agent_id, host=None):
cctxt = self.client.prepare()
cctxt = self.client.prepare(version='1.9')
return cctxt.call(context, 'update_device_down', device=device,
agent_id=agent_id, host=host)
def update_device_up(self, context, device, agent_id, host=None):
cctxt = self.client.prepare()
cctxt = self.client.prepare(version='1.9')
return cctxt.call(context, 'update_device_up', device=device,
agent_id=agent_id, host=host)
def update_device_list(self, context, devices_up, devices_down,
agent_id, host, refresh_tunnels=False):
cctxt = self.client.prepare(version='1.8')
cctxt = self.client.prepare(version='1.9')
ret_devices_up = []
failed_devices_up = []

View File

@ -14,6 +14,7 @@
import netaddr
from neutron_lib import constants
from neutron_lib.db import api as db_api
from neutron_lib.objects import common_types
from neutron_lib.utils import net as net_utils
from oslo_log import log as logging
@ -688,3 +689,19 @@ class Port(base.NeutronDbObject):
return context.session.query(models_v2.Port).filter(
models_v2.IPAllocation.port_id == models_v2.Port.id).filter(
models_v2.IPAllocation.subnet_id == subnet_id).all()
@classmethod
def get_port_from_mac_and_pci_slot(cls, context, device_mac,
pci_slot=None):
with db_api.CONTEXT_READER.using(context):
ports = cls.get_objects(context, mac_address=device_mac)
if not ports:
return
elif not pci_slot:
return ports.pop()
else:
for port in ports:
for _binding in port.bindings:
if _binding.get('profile', {}).get('pci_slot') == pci_slot:
return port

View File

@ -142,13 +142,6 @@ def get_port(context, port_id):
return
@db_api.CONTEXT_READER
def get_port_from_device_mac(context, device_mac):
LOG.debug("get_port_from_device_mac() called for mac %s", device_mac)
ports = port_obj.Port.get_objects(context, mac_address=device_mac)
return ports.pop() if ports else None
def get_ports_and_sgs(context, port_ids):
"""Get ports from database with security group info."""

View File

@ -21,6 +21,7 @@ from neutron_lib.utils import helpers
from oslo_log import log as logging
from neutron._i18n import _
from neutron.agent import rpc as agent_rpc
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
@ -185,7 +186,8 @@ class EmbSwitch(object):
for pci_slot, vf_index in self.pci_slot_map.items():
mac = self.get_pci_device(pci_slot)
if mac:
assigned_devices_info.append((mac, pci_slot))
assigned_devices_info.append(
agent_rpc.DeviceInfo(mac, pci_slot))
return assigned_devices_info
def get_device_state(self, pci_slot):

View File

@ -89,12 +89,9 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin):
# notifications there is no guarantee the notifications are
# processed in the same order as the relevant API requests.
mac = port['mac_address']
pci_slot = None
if port.get(portbindings.PROFILE):
pci_slot = port[portbindings.PROFILE].get('pci_slot')
pci_slot = port.get(portbindings.PROFILE, {}).get('pci_slot')
if pci_slot:
self.agent.updated_devices.add((mac, pci_slot))
self.agent.updated_devices.add(agent_rpc.DeviceInfo(mac, pci_slot))
LOG.debug("port_update RPC received for port: %(id)s with MAC "
"%(mac)s and PCI slot %(pci_slot)s slot",
{'id': port['id'], 'mac': mac, 'pci_slot': pci_slot})
@ -278,97 +275,98 @@ class SriovNicSwitchAgent(object):
# If one of the above operations fails => resync with plugin
return (resync_a | resync_b)
def treat_device(self, device, pci_slot, admin_state_up, spoofcheck=True,
def treat_device(self, device_info, admin_state_up, spoofcheck=True,
propagate_uplink_state=False):
if self.eswitch_mgr.device_exists(device, pci_slot):
if self.eswitch_mgr.device_exists(device_info.mac,
device_info.pci_slot):
try:
self.eswitch_mgr.set_device_spoofcheck(device, pci_slot,
spoofcheck)
self.eswitch_mgr.set_device_spoofcheck(
device_info.mac, device_info.pci_slot, spoofcheck)
except Exception:
LOG.warning("Failed to set spoofcheck for device %s",
device)
device_info)
LOG.info("Device %(device)s spoofcheck %(spoofcheck)s",
{"device": device, "spoofcheck": spoofcheck})
{"device": device_info, "spoofcheck": spoofcheck})
try:
self.eswitch_mgr.set_device_state(device, pci_slot,
admin_state_up,
propagate_uplink_state)
self.eswitch_mgr.set_device_state(
device_info.mac, device_info.pci_slot, admin_state_up,
propagate_uplink_state)
except priv_ip_lib.InterfaceOperationNotSupported:
LOG.warning("Device %s does not support state change", device)
LOG.warning("Device %s does not support state change",
device_info)
except pyroute2.NetlinkError:
LOG.warning("Failed to set device %s state", device)
LOG.warning("Failed to set device %s state", device_info)
return False
else:
LOG.info("No device with MAC %s defined on agent.", device)
LOG.info("No device %s defined on agent.", device_info)
return False
return True
def _update_network_ports(self, network_id, port_id, mac_pci_slot):
self._clean_network_ports(mac_pci_slot)
def _update_network_ports(self, network_id, port_id, device):
self._clean_network_ports(device)
self.network_ports[network_id].append({
"port_id": port_id,
"device": mac_pci_slot})
"device": device})
def _clean_network_ports(self, mac_pci_slot):
for netid, ports_list in self.network_ports.items():
for port_data in ports_list:
if mac_pci_slot == port_data['device']:
def _clean_network_ports(self, device_to_clean):
for netid, ports_list in dict(self.network_ports).items():
for port_data in list(ports_list):
if device_to_clean == port_data['device']:
ports_list.remove(port_data)
if ports_list == []:
if not 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])
devices_details_list = self.plugin_rpc.get_devices_details_list(
self.context, macs_list, self.agent_id, self.conf.host)
rpc_devices_details = self.plugin_rpc.get_devices_details_list(
self.context, devices_info, self.agent_id, self.conf.host)
except Exception as e:
LOG.debug("Unable to get port details for devices "
"with MAC addresses %(devices)s: %(e)s",
{'devices': macs_list, 'e': e})
{'devices': devices_info, 'e': e})
# resync is needed
return True
devices_up = set()
devices_down = set()
resync = False
for device_details in devices_details_list:
device = device_details['device']
LOG.debug("Port with MAC address %s is added", device)
for device_details in rpc_devices_details:
mac_address = device_details['device']
LOG.debug("Port with MAC address %s is added", mac_address)
if 'port_id' in device_details:
LOG.info("Port %(device)s updated. Details: %(details)s",
{'device': device, 'details': device_details})
{'device': mac_address, 'details': device_details})
port_id = device_details['port_id']
profile = device_details['profile']
device_info = agent_rpc.DeviceInfo(mac_address,
profile.get('pci_slot'))
spoofcheck = device_details.get('port_security_enabled', True)
if self.treat_device(
device,
profile.get('pci_slot'),
device_info,
device_details['admin_state_up'],
spoofcheck,
device_details['propagate_uplink_status']):
if device_details['admin_state_up']:
devices_up.add(device)
devices_up.add(device_info)
else:
devices_down.add(device)
devices_down.add(device_info)
else:
resync = True
self._update_network_ports(device_details['network_id'],
port_id,
(device, profile.get('pci_slot')))
port_id, device_info)
self.ext_manager.handle_port(self.context, device_details)
elif n_constants.NO_ACTIVE_BINDING in device_details:
# Port was added but its binding in this agent
# hasn't been activated yet. It will be treated as
# added when binding is activated
LOG.info("Device with MAC %s has no active binding in host",
device)
mac_address)
else:
LOG.info("Device with MAC %s not defined on plugin",
device)
mac_address)
self.plugin_rpc.update_device_list(self.context,
devices_up,
devices_down,
@ -379,39 +377,32 @@ class SriovNicSwitchAgent(object):
def treat_devices_removed(self, devices):
resync = False
for device in devices:
mac, pci_slot = device
LOG.info("Removing device with MAC address %(mac)s and "
"PCI slot %(pci_slot)s",
{'mac': mac, 'pci_slot': pci_slot})
{'mac': device.mac, 'pci_slot': device.pci_slot})
try:
port_id = self._clean_network_ports(device)
if port_id:
port = {'port_id': port_id,
'device': mac,
'profile': {'pci_slot': pci_slot}}
'device': device.mac,
'profile': {'pci_slot': device.pci_slot}}
self.ext_manager.delete_port(self.context, port)
else:
LOG.warning("port_id to device with MAC "
"%s not found", mac)
LOG.warning("port_id to device %s not found", device)
dev_details = self.plugin_rpc.update_device_down(self.context,
mac,
device,
self.agent_id,
cfg.CONF.host)
except Exception as e:
LOG.debug("Removing port failed for device with MAC address "
"%(mac)s and PCI slot %(pci_slot)s due to %(exc)s",
{'mac': mac, 'pci_slot': pci_slot, 'exc': e})
LOG.debug("Removing port failed for device %(device)s due to "
"%(exc)s", {'device': device, 'exc': e})
resync = True
continue
if dev_details['exists']:
LOG.info("Port with MAC %(mac)s and PCI slot "
"%(pci_slot)s updated.",
{'mac': mac, 'pci_slot': pci_slot})
LOG.info("Port from device %s updated", device)
else:
LOG.debug("Device with MAC %(mac)s and PCI slot "
"%(pci_slot)s not defined on plugin",
{'mac': mac, 'pci_slot': pci_slot})
LOG.debug("Device %s not defined on plugin", device)
return resync
def process_activated_bindings(self, device_info, activated_bindings_copy):

View File

@ -2261,7 +2261,7 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
return ports
@staticmethod
def _device_to_port_id(context, device):
def _device_to_port_id(context, device, pci_slot=None):
# REVISIT(rkukura): Consider calling into MechanismDrivers to
# process device names, or having MechanismDrivers supply list
# of device prefixes to strip.
@ -2271,7 +2271,8 @@ class Ml2Plugin(db_base_plugin_v2.NeutronDbPluginV2,
# REVISIT(irenab): Consider calling into bound MD to
# handle the get_device_details RPC
if not uuidutils.is_uuid_like(device):
port = db.get_port_from_device_mac(context, device)
port = ports_obj.Port.get_port_from_mac_and_pci_slot(
context, device, pci_slot=pci_slot)
if port:
return port.id
return device

View File

@ -57,8 +57,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
# 1.7 Support get_ports_by_vnic_type_and_host
# 1.8 Rename agent_restarted to refresh_tunnels in
# update_device_list to reflect its expanded purpose
target = oslo_messaging.Target(version='1.8')
# 1.9 Support for device definition as DeviceInfo(mac, pci_info) for:
# - get_device_details
# - get_devices_details_list (indirectly, calls get_device_details)
# - update_device_down
# - update_device_up
# - update_device_list (indirectly, called from update_device_down
# and update_device_up)
target = oslo_messaging.Target(version='1.9')
def __init__(self, notifier, type_manager):
self.setup_tunnel_callback_mixin(notifier, type_manager)
@ -78,6 +84,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
kwargs.get('host'),
kwargs.get('device') or kwargs.get('network'))
@staticmethod
def _device_to_mac_pci_slot(device):
"""This method will keep backwards compatibility with agents < 1.9"""
# NOTE(ralonsoh): this method can be removed in Z release.
if isinstance(device, list): # RPC loads from agent_rpc.DeviceInfo
return device[0], device[1]
return device, None
def get_device_details(self, rpc_context, **kwargs):
"""Agent requests device details."""
agent_id, host, device = self._get_request_details(kwargs)
@ -90,7 +104,9 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
{'device': device, 'agent_id': agent_id, 'host': host})
plugin = directory.get_plugin()
port_id = plugin._device_to_port_id(rpc_context, device)
mac_or_port_id, pci_slot = self._device_to_mac_pci_slot(device)
port_id = plugin._device_to_port_id(rpc_context, mac_or_port_id,
pci_slot=pci_slot)
port_context = plugin.get_bound_port_context(rpc_context,
port_id,
host,
@ -98,8 +114,8 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
if not port_context:
LOG.debug("Device %(device)s requested by agent "
"%(agent_id)s not found in database",
{'device': device, 'agent_id': agent_id})
return {'device': device}
{'device': mac_or_port_id, 'agent_id': agent_id})
return {'device': mac_or_port_id}
port = port_context.current
# caching information about networks for future use
@ -108,7 +124,7 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
cached_networks[port['network_id']] = (
port_context.network.current)
result = self._get_device_details(rpc_context, agent_id=agent_id,
host=host, device=device,
host=host, device=mac_or_port_id,
port_context=port_context)
if 'network_id' in result:
# success so we update status
@ -244,13 +260,15 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
"%(agent_id)s",
{'device': device, 'agent_id': agent_id})
plugin = directory.get_plugin()
port_id = plugin._device_to_port_id(rpc_context, device)
mac_or_device, pci_slot = self._device_to_mac_pci_slot(device)
port_id = plugin._device_to_port_id(rpc_context, mac_or_device,
pci_slot=pci_slot)
port_exists = True
if (host and not plugin.port_bound_to_host(rpc_context,
port_id, host)):
LOG.debug("Device %(device)s not bound to the"
" agent host %(host)s",
{'device': device, 'host': host})
{'device': mac_or_device, 'host': host})
else:
try:
port_exists = bool(plugin.update_port_status(
@ -259,12 +277,12 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
port_exists = False
LOG.debug("delete_port and update_device_down are being "
"executed concurrently. Ignoring StaleDataError.")
return {'device': device,
return {'device': mac_or_device,
'exists': port_exists}
self.notify_l2pop_port_wiring(port_id, rpc_context,
n_const.PORT_STATUS_DOWN, host)
return {'device': device,
return {'device': mac_or_device,
'exists': port_exists}
@profiler.trace("rpc")
@ -278,12 +296,14 @@ class RpcCallbacks(type_tunnel.TunnelRpcCallbackMixin):
LOG.debug("Device %(device)s up at agent %(agent_id)s",
{'device': device, 'agent_id': agent_id})
plugin = directory.get_plugin()
port_id = plugin._device_to_port_id(rpc_context, device)
mac_or_device, pci_slot = self._device_to_mac_pci_slot(device)
port_id = plugin._device_to_port_id(rpc_context, mac_or_device,
pci_slot=pci_slot)
port = plugin.port_bound_to_host(rpc_context, port_id, host)
if host and not port:
LOG.debug("Device %(device)s not bound to the"
" agent host %(host)s",
{'device': device, 'host': host})
{'device': mac_or_device, 'host': host})
# this might mean that a VM is in the process of live migration
# and vif was plugged on the destination compute node;
# need to notify nova explicitly

View File

@ -13,6 +13,7 @@
from unittest import mock
import netaddr
from neutron_lib.api.definitions import portbindings
from neutron_lib import constants
from neutron_lib.tests import tools
from oslo_utils import uuidutils
@ -609,3 +610,34 @@ class PortDbObjectTestCase(obj_test_base.BaseDbObjectTestCase,
(0, 1),
(len(dhcp_ports), count),
)
def test_get_port_from_mac_and_pci_slot_no_ports(self):
self.assertIsNone(
ports.Port.get_port_from_mac_and_pci_slot(self.context,
'ca:fe:ca:fe:ca:fe'))
def test_get_port_from_mac_and_pci_slot_no_pci_slot(self):
obj = self._make_object(self.obj_fields[0])
obj.create()
mac_address = obj.mac_address
port = ports.Port.get_port_from_mac_and_pci_slot(self.context,
mac_address)
self.assertEqual(obj.id, port.id)
def test_get_port_from_mac_and_pci_slot(self):
obj = self._make_object(self.obj_fields[0])
obj.create()
mac_address = obj.mac_address
pci_slot = '0000:04:00.1'
port = ports.Port.get_port_from_mac_and_pci_slot(
self.context, mac_address, pci_slot=pci_slot)
self.assertIsNone(port)
port_binding = ports.PortBinding(
self.context, port_id=obj.id, host='any_host',
vif_type=portbindings.VIF_TYPE_OTHER,
vnic_type=portbindings.VNIC_DIRECT, profile={'pci_slot': pci_slot})
port_binding.create()
port = ports.Port.get_port_from_mac_and_pci_slot(
self.context, mac_address, pci_slot=pci_slot)
self.assertEqual(obj.id, port.id)

View File

@ -16,9 +16,11 @@
import os
from unittest import mock
from neutron.agent import rpc as agent_rpc
from neutron.plugins.ml2.drivers.mech_sriov.agent.common \
import exceptions as exc
from neutron.plugins.ml2.drivers.mech_sriov.agent import eswitch_manager as esm
from neutron.plugins.ml2.drivers.mech_sriov.agent import pci_lib
from neutron.tests import base
@ -429,51 +431,38 @@ class TestEmbSwitch(base.BaseTestCase):
return_value=self.SCANNED_DEVICES):
self.emb_switch = esm.EmbSwitch(self.DEV_NAME, exclude_devices)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.scan_vf_devices",
return_value=[(PCI_SLOT, 0)])
def test_get_assigned_devices_info(self, *args):
emb_switch = esm.EmbSwitch(self.DEV_NAME, ())
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
return_value={0: self.ASSIGNED_MAC}),\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.pf_device_exists",
return_value=True), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper."
"is_assigned_vf_direct", return_value=True):
result = emb_switch.get_assigned_devices_info()
self.assertIn(self.ASSIGNED_MAC, list(result)[0])
self.assertIn(self.PCI_SLOT, list(result)[0])
def test_get_assigned_devices_info(self):
with mock.patch.object(pci_lib.PciDeviceIPWrapper, 'get_assigned_macs',
return_value={0: self.ASSIGNED_MAC}), \
mock.patch.object(esm.PciOsWrapper, 'pf_device_exists',
return_value=True), \
mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct',
return_value=True):
result = self.emb_switch.get_assigned_devices_info()
device_info = agent_rpc.DeviceInfo(self.ASSIGNED_MAC,
self.PCI_SLOT)
self.assertEqual(1, len(result))
self.assertEqual(device_info, result[0])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.scan_vf_devices",
return_value=SCANNED_DEVICES)
def test_get_assigned_devices_info_multiple_slots(self, *args):
emb_switch = esm.EmbSwitch(self.DEV_NAME, ())
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
return_value=self.VF_TO_MAC_MAPPING),\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper."
"is_assigned_vf_direct", return_value=True):
devices_info = emb_switch.get_assigned_devices_info()
def test_get_assigned_devices_info_multiple_slots(self):
with mock.patch.object(pci_lib.PciDeviceIPWrapper, 'get_assigned_macs',
return_value=self.VF_TO_MAC_MAPPING), \
mock.patch.object(esm.PciOsWrapper, 'pf_device_exists',
return_value=True), \
mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct',
return_value=True):
devices_info = self.emb_switch.get_assigned_devices_info()
for device_info in devices_info:
mac = device_info[0]
pci_slot = device_info[1]
self.assertEqual(
self.EXPECTED_MAC_TO_PCI[mac], pci_slot)
self.assertEqual(self.EXPECTED_MAC_TO_PCI[device_info.mac],
device_info.pci_slot)
def test_get_assigned_devices_empty(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf_direct",
return_value=False), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper."
"is_assigned_vf_macvtap", return_value=False):
with mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_direct',
return_value=False), \
mock.patch.object(esm.PciOsWrapper, 'is_assigned_vf_macvtap',
return_value=False):
result = self.emb_switch.get_assigned_devices_info()
self.assertFalse(result)
self.assertEqual([], result)
def test_get_device_state_ok(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."

View File

@ -13,6 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import copy
from unittest import mock
from neutron_lib.api.definitions import portbindings
@ -30,6 +31,30 @@ from neutron.tests import base
DEVICE_MAC = '11:22:33:44:55:66'
PCI_SLOT = "0000:06:00.1"
DEV1 = agent_rpc.DeviceInfo('mac1', 'pci_slot1')
DEV2 = agent_rpc.DeviceInfo('mac2', 'pci_slot2')
DEV3 = agent_rpc.DeviceInfo('mac3', 'pci_slot3')
DEV4 = agent_rpc.DeviceInfo('mac4', 'pci_slot4')
RPC_DEV1 = {'device': DEV1.mac,
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': DEV1.pci_slot},
'physical_network': 'physnet1',
'port_security_enabled': False}
RPC_DEV2 = {'device': DEV2.mac,
'port_id': 'port321',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': DEV2.pci_slot},
'physical_network': 'physnet1',
'port_security_enabled': False}
class TestSriovAgent(base.BaseTestCase):
@ -78,7 +103,7 @@ class TestSriovAgent(base.BaseTestCase):
def test_treat_devices_removed_with_existed_device(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {})
devices = [(DEVICE_MAC, PCI_SLOT)]
devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)}
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.return_value = {'device': DEVICE_MAC,
@ -89,7 +114,7 @@ class TestSriovAgent(base.BaseTestCase):
def test_treat_devices_removed_with_not_existed_device(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {})
devices = [(DEVICE_MAC, PCI_SLOT)]
devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)}
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.return_value = {'device': DEVICE_MAC,
@ -103,7 +128,7 @@ class TestSriovAgent(base.BaseTestCase):
def test_treat_devices_removed_failed(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}, {})
devices = [(DEVICE_MAC, PCI_SLOT)]
devices = {agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)}
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.side_effect = Exception()
@ -134,51 +159,51 @@ class TestSriovAgent(base.BaseTestCase):
self.mock_scan_devices(expected, mock_current, registered, updated)
def test_scan_devices_no_changes(self):
registered = set(['1', '2'])
registered = {DEV1, DEV2}
updated = set()
mock_current = set(['1', '2'])
expected = {'current': set(['1', '2']),
mock_current = {DEV1, DEV2}
expected = {'current': {DEV1, DEV2},
'updated': set(),
'added': set(),
'removed': set()}
self.mock_scan_devices(expected, mock_current, registered, updated)
def test_scan_devices_new_and_removed(self):
registered = set(['1', '2'])
registered = {DEV1, DEV2}
updated = set()
mock_current = set(['2', '3'])
expected = {'current': set(['2', '3']),
mock_current = {DEV2, DEV3}
expected = {'current': {DEV2, DEV3},
'updated': set(),
'added': set(['3']),
'removed': set(['1'])}
'added': {DEV3},
'removed': {DEV1}}
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']),
registered = {DEV1, DEV2}
# 'DEV1' is in removed and updated tuple
updated = {DEV1}
mock_current = {DEV2, DEV3}
expected = {'current': {DEV2, DEV3},
'updated': set(),
'added': set(['3']),
'removed': set(['1'])}
'added': {DEV3},
'removed': {DEV1}}
self.mock_scan_devices(expected, mock_current, registered, updated)
def test_scan_devices_new_updates(self):
registered = set(['1'])
updated = set(['2'])
mock_current = set(['1', '2'])
expected = {'current': set(['1', '2']),
'updated': set(['2']),
'added': set(['2']),
registered = {DEV1}
updated = {DEV2}
mock_current = {DEV1, DEV2}
expected = {'current': {DEV1, DEV2},
'updated': {DEV2},
'added': {DEV2},
'removed': set()}
self.mock_scan_devices(expected, mock_current, registered, updated)
def test_scan_devices_updated_missing(self):
registered = set(['1'])
updated = set(['2'])
mock_current = set(['1'])
expected = {'current': set(['1']),
registered = {DEV1}
updated = {DEV2}
mock_current = {DEV1}
expected = {'current': {DEV1},
'updated': set(),
'added': set(),
'removed': set()}
@ -187,9 +212,9 @@ class TestSriovAgent(base.BaseTestCase):
def test_process_network_devices(self):
agent = self.agent
device_info = {'current': set(),
'added': set(['mac3', 'mac4']),
'updated': set(['mac2', 'mac3']),
'removed': set(['mac1'])}
'added': {DEV3, DEV4},
'updated': {DEV2, DEV3},
'removed': {DEV1}}
agent.sg_agent.prepare_devices_filter = mock.Mock()
agent.sg_agent.refresh_firewall = mock.Mock()
agent.treat_devices_added_updated = mock.Mock(return_value=False)
@ -197,226 +222,115 @@ class TestSriovAgent(base.BaseTestCase):
agent.process_network_devices(device_info)
agent.sg_agent.prepare_devices_filter.assert_called_with(
set(['mac3', 'mac4']))
agent.sg_agent.prepare_devices_filter.assert_called_with({DEV3, DEV4})
self.assertTrue(agent.sg_agent.refresh_firewall.called)
agent.treat_devices_added_updated.assert_called_with(set(['mac2',
'mac3',
'mac4']))
agent.treat_devices_removed.assert_called_with(set(['mac1']))
agent.treat_devices_added_updated.assert_called_with(
{DEV2, DEV3, DEV4})
agent.treat_devices_removed.assert_called_with({DEV1})
def test_treat_devices_added_updated_sends_host(self):
agent = self.agent
host = 'host1'
cfg.CONF.set_override('host', host)
agent.plugin_rpc = mock.Mock()
MAC = 'aa:bb:cc:dd:ee:ff'
device_details = {'device': MAC,
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': True,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False}
agent.plugin_rpc.get_devices_details_list.return_value = (
[device_details])
agent.treat_devices_added_updated([[MAC]])
agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1]
devices = {agent_rpc.DeviceInfo(DEV1.mac, DEV1.pci_slot)}
agent.treat_devices_added_updated(devices)
agent.plugin_rpc.get_devices_details_list.assert_called_once_with(
mock.ANY, set([MAC]), mock.ANY, host)
agent.context, devices, agent.agent_id, host)
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,
'propagate_uplink_status': False,
'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,
'propagate_uplink_status': False,
'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.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1]
agent.treat_devices_added_updated({DEV1})
self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1}]},
agent.network_ports)
agent.plugin_rpc.get_devices_details_list.return_value = (
[mock_device2_details])
agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV2]
# 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])
agent.treat_devices_added_updated({DEV2})
self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1},
{'port_id': 'port321', 'device': DEV2}]},
agent.network_ports)
with mock.patch.object(agent.plugin_rpc, "update_device_down"):
agent.treat_devices_removed({DEV2})
# remove the second device and check the network_ports dict
self.assertEqual({'net123': [{'port_id': 'port123',
'device': mac_pci_slot_device1}]},
self.assertEqual({'net123': [{'port_id': 'port123', 'device': DEV1}]},
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',
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False}
agent.plugin_rpc = mock.Mock()
agent.plugin_rpc.get_devices_details_list.return_value = [mock_details]
agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1]
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True
agent.set_device_state = mock.Mock()
agent.set_device_spoofcheck = mock.Mock()
resync_needed = agent.treat_devices_added_updated(
set(['aa:bb:cc:dd:ee:ff']))
devices = {DEV1}
resync_needed = agent.treat_devices_added_updated(devices)
self.assertFalse(resync_needed)
agent.eswitch_mgr.device_exists.assert_called_with('aa:bb:cc:dd:ee:ff',
'1:2:3.0')
agent.eswitch_mgr.device_exists.assert_called_with(DEV1.mac,
DEV1.pci_slot)
agent.eswitch_mgr.set_device_state.assert_called_with(
'aa:bb:cc:dd:ee:ff',
'1:2:3.0',
True, False)
DEV1.mac, DEV1.pci_slot, True, False)
agent.eswitch_mgr.set_device_spoofcheck.assert_called_with(
'aa:bb:cc:dd:ee:ff',
'1:2:3.0',
False)
DEV1.mac, DEV1.pci_slot, False)
agent.plugin_rpc.update_device_list.assert_called_once_with(
mock.ANY,
set(['aa:bb:cc:dd:ee:ff']),
set(),
mock.ANY,
mock.ANY)
agent.context, devices, set(), agent.agent_id, agent.conf.host)
def test_treat_devices_added_updated_multiple_admin_state_up_true(self):
agent = self.agent
mock_details = [{'device': 'aa:bb:cc:dd:ee:ff',
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False},
{'device': '11:22:33:44:55:66',
'port_id': 'port321',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False}]
mock_details = [RPC_DEV1, RPC_DEV2]
agent.plugin_rpc = mock.Mock()
agent.plugin_rpc.get_devices_details_list.return_value = mock_details
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True
agent.set_device_state = mock.Mock()
agent.set_device_spoofcheck = mock.Mock()
resync_needed = agent.treat_devices_added_updated(
set(['aa:bb:cc:dd:ee:ff',
'11:22:33:44:55:66']))
devices = {DEV1, DEV2}
resync_needed = agent.treat_devices_added_updated(devices)
self.assertFalse(resync_needed)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'),
mock.call('11:22:33:44:55:66', '1:2:3.0')]
calls = [mock.call(DEV1.mac, DEV1.pci_slot),
mock.call(DEV2.mac, DEV2.pci_slot)]
agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True, False),
mock.call('11:22:33:44:55:66', '1:2:3.0', True, False)]
calls = [mock.call(DEV1.mac, DEV1.pci_slot, True, False),
mock.call(DEV2.mac, DEV2.pci_slot, True, False)]
agent.eswitch_mgr.set_device_state.assert_has_calls(calls,
any_order=True)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False),
mock.call('11:22:33:44:55:66', '1:2:3.0', False)]
agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls,
any_order=True)
calls = [mock.call(DEV1.mac, DEV1.pci_slot, False),
mock.call(DEV2.mac, DEV2.pci_slot, False)]
agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(
calls, any_order=True)
agent.plugin_rpc.update_device_list.assert_called_once_with(
mock.ANY,
set(['aa:bb:cc:dd:ee:ff', '11:22:33:44:55:66']),
set(),
mock.ANY,
mock.ANY)
agent.context, devices, set(), agent.agent_id, agent.conf.host)
def test_treat_devices_added_updated_multiple_admin_states(self):
agent = self.agent
mock_details = [{'device': 'aa:bb:cc:dd:ee:ff',
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False},
{'device': '11:22:33:44:55:66',
'port_id': 'port321',
'network_id': 'net123',
'admin_state_up': False,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1',
'port_security_enabled': False}]
rpc_dev2 = copy.deepcopy(RPC_DEV2)
rpc_dev2['admin_state_up'] = False
mock_details = [RPC_DEV1, rpc_dev2]
agent.plugin_rpc = mock.Mock()
agent.plugin_rpc.get_devices_details_list.return_value = mock_details
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True
agent.set_device_state = mock.Mock()
agent.set_device_spoofcheck = mock.Mock()
resync_needed = agent.treat_devices_added_updated(
set(['aa:bb:cc:dd:ee:ff',
'11:22:33:44:55:66']))
devices = {DEV1, DEV2}
resync_needed = agent.treat_devices_added_updated(devices)
self.assertFalse(resync_needed)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0'),
mock.call('11:22:33:44:55:66', '1:2:3.0')]
calls = [mock.call(DEV1.mac, DEV1.pci_slot),
mock.call(DEV2.mac, DEV2.pci_slot)]
agent.eswitch_mgr.device_exists.assert_has_calls(calls, any_order=True)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', True, False),
mock.call('11:22:33:44:55:66', '1:2:3.0', False, False)]
calls = [mock.call(DEV1.mac, DEV1.pci_slot, True, False),
mock.call(DEV2.mac, DEV2.pci_slot, False, False)]
agent.eswitch_mgr.set_device_state.assert_has_calls(calls,
any_order=True)
calls = [mock.call('aa:bb:cc:dd:ee:ff', '1:2:3.0', False),
mock.call('11:22:33:44:55:66', '1:2:3.0', False)]
agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(calls,
any_order=True)
calls = [mock.call(DEV1.mac, DEV1.pci_slot, False),
mock.call(DEV2.mac, DEV2.pci_slot, False)]
agent.eswitch_mgr.set_device_spoofcheck.assert_has_calls(
calls, any_order=True)
agent.plugin_rpc.update_device_list.assert_called_once_with(
mock.ANY,
set(['aa:bb:cc:dd:ee:ff']),
set(['11:22:33:44:55:66']),
mock.ANY,
mock.ANY)
agent.context, {DEV1}, {DEV2}, agent.agent_id, agent.conf.host)
def test_treat_device_ip_link_state_not_supported(self):
agent = self.agent
@ -426,8 +340,7 @@ class TestSriovAgent(base.BaseTestCase):
agent.eswitch_mgr.set_device_state.side_effect = (
priv_ip_lib.InterfaceOperationNotSupported())
self.assertTrue(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0',
admin_state_up=True))
self.assertTrue(agent.treat_device(DEV1, admin_state_up=True))
def test_treat_device_set_device_state_exception(self):
agent = self.agent
@ -437,8 +350,7 @@ class TestSriovAgent(base.BaseTestCase):
agent.eswitch_mgr.set_device_state.side_effect = (
pyroute2.NetlinkError(22))
self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0',
admin_state_up=True))
self.assertFalse(agent.treat_device(DEV1, admin_state_up=True))
def test_treat_device_no_device_found(self):
agent = self.agent
@ -446,53 +358,32 @@ class TestSriovAgent(base.BaseTestCase):
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = False
self.assertFalse(agent.treat_device('aa:bb:cc:dd:ee:ff', '1:2:3:0',
admin_state_up=True))
self.assertFalse(agent.treat_device(DEV1, admin_state_up=True))
def test_treat_devices_added_updated_admin_state_up_false(self):
agent = self.agent
mock_details = {'device': 'aa:bb:cc:dd:ee:ff',
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': False,
'propagate_uplink_status': False,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'physical_network': 'physnet1'}
agent.plugin_rpc = mock.Mock()
agent.plugin_rpc.get_devices_details_list.return_value = [mock_details]
rpc_dev1 = copy.deepcopy(RPC_DEV1)
rpc_dev1['admin_state_up'] = False
agent.plugin_rpc.get_devices_details_list.return_value = [rpc_dev1]
agent.remove_port_binding = mock.Mock()
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = True
resync_needed = agent.treat_devices_added_updated(
set(['aa:bb:cc:dd:ee:ff']))
devices = {DEV1}
resync_needed = agent.treat_devices_added_updated(devices)
self.assertFalse(resync_needed)
agent.plugin_rpc.update_device_list.assert_called_once_with(
mock.ANY,
set(),
set(['aa:bb:cc:dd:ee:ff']),
mock.ANY,
mock.ANY)
agent.context, set(), devices, agent.agent_id, agent.conf.host)
def test_treat_devices_added_updated_no_device_found(self):
agent = self.agent
mock_details = {'device': 'aa:bb:cc:dd:ee:ff',
'port_id': 'port123',
'network_id': 'net123',
'admin_state_up': True,
'network_type': 'vlan',
'segmentation_id': 100,
'profile': {'pci_slot': '1:2:3.0'},
'propagate_uplink_status': False,
'physical_network': 'physnet1'}
agent.plugin_rpc = mock.Mock()
agent.plugin_rpc.get_devices_details_list.return_value = [mock_details]
agent.plugin_rpc.get_devices_details_list.return_value = [RPC_DEV1]
agent.remove_port_binding = mock.Mock()
agent.eswitch_mgr = mock.Mock()
agent.eswitch_mgr.device_exists.return_value = False
resync_needed = agent.treat_devices_added_updated(
set(['aa:bb:cc:dd:ee:ff']))
devices = {DEV1}
resync_needed = agent.treat_devices_added_updated(devices)
self.assertTrue(resync_needed)
self.assertFalse(agent.plugin_rpc.update_device_up.called)
@ -502,27 +393,20 @@ class TestSriovAgent(base.BaseTestCase):
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_id1, port_id1, DEV1)
self.agent._update_network_ports(network_id1, port_id2, DEV2)
self.agent._update_network_ports(network_id2, port_id1, mac_slot_1)
self.agent._update_network_ports(network_id2, port_id1, DEV1)
expected = {network_id1: [{'port_id': port_id2, 'device': DEV2}],
network_id2: [{'port_id': port_id1, 'device': DEV1}]}
self.assertEqual(expected, dict(self.agent.network_ports))
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)
self.assertEqual(port_id1, self.agent._clean_network_ports(DEV1))
expected = {network_id1: [{'port_id': port_id2, 'device': DEV2}]}
self.assertEqual(expected, 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(port_id2, self.agent._clean_network_ports(DEV2))
self.assertEqual({}, self.agent.network_ports)
def test_configurations_has_rp_bandwidth(self):
@ -558,17 +442,12 @@ class TestSriovAgent(base.BaseTestCase):
rp_inv_defaults[inv_key])
def test_process_activated_bindings(self):
# Create several devices which are pairs of (<mac_addr>, <pci_slot>)
dev_a = ('fa:16:3e:f8:ae:af', "0000:01:00.0")
dev_b = ('fa:16:3e:f8:ae:b0', "0000:02:00.0")
dev_c = ('fa:16:3e:f8:ae:b1', "0000:03:00.0")
# Create device_info
fake_device_info = {
'current': set([dev_a, dev_b]),
'added': set([dev_c]),
'current': {DEV1, DEV2},
'added': {DEV3},
'removed': set(),
'updated': set()}
fake_activated_bindings = set([dev_a])
fake_activated_bindings = {DEV1}
self.agent.process_activated_bindings(fake_device_info,
fake_activated_bindings)
self.assertLessEqual(fake_activated_bindings,
@ -592,6 +471,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
sg_agent = object()
self.sriov_rpc_callback = sriov_nic_agent.SriovNicSwitchRpcCallbacks(
self.context, self.agent, sg_agent)
self.device_info = agent_rpc.DeviceInfo(DEVICE_MAC, PCI_SLOT)
def _create_fake_port(self):
return {'id': uuidutils.generate_uuid(),
@ -606,8 +486,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
port = self._create_fake_port()
kwargs = {'context': self.context, 'port': port}
self.sriov_rpc_callback.port_update(**kwargs)
self.assertEqual(set([(DEVICE_MAC, PCI_SLOT)]),
self.agent.updated_devices)
self.assertEqual({self.device_info}, self.agent.updated_devices)
def test_port_update_with_vnic_physical_direct(self):
port = self._create_fake_port()
@ -618,7 +497,7 @@ class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
def test_port_update_without_pci_slot(self):
port = self._create_fake_port()
port[portbindings.PROFILE] = None
port[portbindings.PROFILE] = {}
kwargs = {'context': self.context, 'port': port}
self.sriov_rpc_callback.port_update(**kwargs)
self.assertEqual(set(), self.agent.updated_devices)

View File

@ -280,16 +280,6 @@ class Ml2DBTestCase(testlib_api.SqlTestCase):
port = ml2_db.get_port(self.ctx, port_id)
self.assertIsNone(port)
def test_get_port_from_device_mac(self):
network_id = uuidutils.generate_uuid()
port_id = uuidutils.generate_uuid()
self._setup_neutron_network(network_id)
port = self._setup_neutron_port(network_id, port_id)
observed_port = ml2_db.get_port_from_device_mac(self.ctx,
port['mac_address'])
self.assertEqual(port_id, observed_port.id)
def test_generating_multiple_mac_addresses(self):
mac_regex = "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$"

View File

@ -436,7 +436,8 @@ class RpcApiTestCase(base.BaseTestCase):
'get_device_details', rpc_method='call',
device='fake_device',
agent_id='fake_agent_id',
host='fake_host')
host='fake_host',
version='1.9')
def test_devices_details_list(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
@ -444,7 +445,7 @@ class RpcApiTestCase(base.BaseTestCase):
'get_devices_details_list', rpc_method='call',
devices=['fake_device1', 'fake_device2'],
agent_id='fake_agent_id', host='fake_host',
version='1.3')
version='1.9')
def test_update_device_down(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
@ -452,7 +453,8 @@ class RpcApiTestCase(base.BaseTestCase):
'update_device_down', rpc_method='call',
device='fake_device',
agent_id='fake_agent_id',
host='fake_host')
host='fake_host',
version='1.9')
def test_tunnel_sync(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
@ -469,7 +471,8 @@ class RpcApiTestCase(base.BaseTestCase):
'update_device_up', rpc_method='call',
device='fake_device',
agent_id='fake_agent_id',
host='fake_host')
host='fake_host',
version='1.9')
def test_update_device_list(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)
@ -480,7 +483,7 @@ class RpcApiTestCase(base.BaseTestCase):
agent_id='fake_agent_id',
host='fake_host',
refresh_tunnels=False,
version='1.8')
version='1.9')
def test_get_devices_details_list_and_failed_devices(self):
rpcapi = agent_rpc.PluginApi(topics.PLUGIN)

View File

@ -0,0 +1,20 @@
---
features:
- |
SR-IOV agent now can handle ports from different networks with the same
MAC addresses. This feature implies an upgrade in the agent and the server
RPC version (see ``neutron.plugins.ml2.rpc.RpcCallbacks`` version 1.9).
Some agent RPC methods have been updated to pass not only the device MAC
address but the PCI slot too. In case of having more than one port with
the same MAC address, the PCI slot will discriminate the requested port.
upgrade:
- |
Both the server and the agent RPC versions have been bumped to 1.9; to
provide a smooth upgrade transition, the `Upgrade Procedure
<https://docs.openstack.org/operations-guide/ops-upgrades.html>`_ should
be followed, upgrading first the servers and then the agents.
The agent RPC methods returned values are not modified to keep
compatibility with other agents (Linux Bridge, Open vSwitch). The RPC
server side is capable of attending calls from agent API < 1.9, in order to
provide backwards compatibility. If the device PCI slot is not provided,
the behavior will be the previous one.