SR-IOV agent can handle ports with same MAC addresses
SR-IOV agent can handle ports with same MAC address (located in different networks). The agent can retrieve, from the system, the MAC address and the PCI slot; because the PCI slot is unique per port in the same host, this parameter is used to match with the Neutron port ID stored in the database (published via RPC). RPC API bumped to version 1.9. Closes-Bug: #1791159 Change-Id: Id8c3e0485bebc55c778ecaadaabca1c28ec56205
This commit is contained in:
parent
7e98d18927
commit
77ac42d2ee
@ -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 = []
|
||||
|
@ -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
|
||||
|
@ -143,13 +143,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."""
|
||||
|
||||
|
@ -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):
|
||||
|
@ -88,12 +88,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})
|
||||
@ -277,97 +274,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,
|
||||
@ -378,39 +376,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):
|
||||
|
@ -2251,7 +2251,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.
|
||||
@ -2261,7 +2261,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
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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."
|
||||
|
@ -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)
|
||||
|
@ -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})$"
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user