Merge "SR-IOV agent can handle ports with same MAC addresses"
This commit is contained in:
commit
4b0a225e80
@ -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
|
||||
|
@ -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."""
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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