"""Get list of VF addresses."""
return self.pci_slot_map.keys()
- def get_assigned_devices(self):
- """Get assigned Virtual Functions.
+ def get_assigned_devices_info(self):
+ """Get assigned Virtual Functions mac and pci slot
+ information and populates vf_to_pci_slot mappings
- @return: list of VF mac addresses
+ @return: list of VF pair (mac address, pci slot)
"""
- vf_list = []
- assigned_macs = []
- for vf_index in self.pci_slot_map.values():
+ vf_to_pci_slot_mapping = {}
+ assigned_devices_info = []
+ for pci_slot, vf_index in self.pci_slot_map.items():
if not PciOsWrapper.is_assigned_vf(self.dev_name, vf_index):
continue
- vf_list.append(vf_index)
- if vf_list:
- assigned_macs = self.pci_dev_wrapper.get_assigned_macs(vf_list)
- return assigned_macs
+ vf_to_pci_slot_mapping[vf_index] = pci_slot
+ if vf_to_pci_slot_mapping:
+ vf_to_mac_mapping = self.pci_dev_wrapper.get_assigned_macs(
+ list(vf_to_pci_slot_mapping.keys()))
+ for vf_index, mac in vf_to_mac_mapping.items():
+ pci_slot = vf_to_pci_slot_mapping[vf_index]
+ assigned_devices_info.append((mac, pci_slot))
+ return assigned_devices_info
def get_device_state(self, pci_slot):
"""Get device state.
if vf_index is not None:
if PciOsWrapper.is_assigned_vf(self.dev_name, vf_index):
macs = self.pci_dev_wrapper.get_assigned_macs([vf_index])
- if macs:
- mac = macs[0]
+ mac = macs.get(vf_index)
return mac
return True
return False
- def get_assigned_devices(self, phys_net=None):
+ def get_assigned_devices_info(self, phys_net=None):
"""Get all assigned devices.
Get all assigned devices belongs to given embedded switch
@param phys_net: physical network, if none get all assigned devices
- @return: set of assigned VFs mac addresses
+ @return: set of assigned VFs (mac address, pci slot) pair
"""
if phys_net:
embedded_switch = self.emb_switches_map.get(phys_net, None)
eswitch_objects = self.emb_switches_map.values()
assigned_devices = set()
for embedded_switch in eswitch_objects:
- for device_mac in embedded_switch.get_assigned_devices():
- assigned_devices.add(device_mac)
+ for device in embedded_switch.get_assigned_devices_info():
+ assigned_devices.add(device)
return assigned_devices
def get_device_state(self, device_mac, pci_slot):
Get the device state (up/True or down/False)
@param device_mac: device mac
- @param pci_slot: VF pci slot
+ @param pci_slot: VF PCI slot
@return: device state (True/False) None if failed
"""
embedded_switch = self._get_emb_eswitch(device_mac, pci_slot)
embedded_switch = None
return embedded_switch
- def get_pci_slot_by_mac(self, device_mac):
- """Get pci slot by mac.
+ def clear_max_rate(self, pci_slot):
+ """Clear the max rate
- Get pci slot by device mac
- @param device_mac: device mac
+ Clear the max rate configuration from VF by setting it to 0
+ @param pci_slot: VF PCI slot
"""
- result = None
- for pci_slot, embedded_switch in self.pci_slot_map.items():
- used_device_mac = embedded_switch.get_pci_device(pci_slot)
- if used_device_mac == device_mac:
- result = pci_slot
- break
- return result
+ #(Note): we don't use the self._get_emb_eswitch here, because when
+ #clearing the VF it may be not assigned. This happens when libvirt
+ #releases the VF back to the hypervisor on delete VM. Therefore we
+ #should just clear the VF max rate according to pci_slot no matter
+ #if VF is assigned or not.
+ embedded_switch = self.pci_slot_map.get(pci_slot)
+ if embedded_switch:
+ #(Note): check the pci_slot is not assigned to some
+ # other port before resetting the max rate.
+ if embedded_switch.get_pci_device(pci_slot) is None:
+ embedded_switch.set_device_max_rate(pci_slot, 0)
+ else:
+ LOG.warning(_LW("VF with PCI slot %(pci_slot)s is already "
+ "assigned; skipping reset maximum rate"),
+ {'pci_slot': pci_slot})
+ else:
+ LOG.error(_LE("PCI slot %(pci_slot)s has no mapping to Embedded "
+ "Switch; skipping"), {'pci_slot': pci_slot})
def _delete_bandwidth_limit(self, port):
pci_slot = port['profile'].get('pci_slot')
- device = port['device']
- self._set_vf_max_rate(device, pci_slot)
+ self.eswitch_mgr.clear_max_rate(pci_slot)
def _set_vf_max_rate(self, device, pci_slot, max_kbps=0):
if self.eswitch_mgr.device_exists(device, pci_slot):
"""Get assigned mac addresses for vf list.
@param vf_list: list of vf indexes
- @return: list of assigned mac addresses
+ @return: dict mapping of vf to mac
"""
try:
out = self._as_root([], "link", ("show", self.dev_name))
LOG.exception(_LE("Failed executing ip command"))
raise exc.IpCommandError(dev_name=self.dev_name,
reason=e)
+ vf_to_mac_mapping = {}
vf_lines = self._get_vf_link_show(vf_list, out)
- vf_details_list = []
if vf_lines:
for vf_line in vf_lines:
vf_details = self._parse_vf_link_show(vf_line)
if vf_details:
- vf_details_list.append(vf_details)
- return [details.get("MAC") for details in vf_details_list]
+ vf_num = vf_details.get('vf')
+ vf_mac = vf_details.get("MAC")
+ vf_to_mac_mapping[vf_num] = vf_mac
+ return vf_to_mac_mapping
def get_vf_state(self, vf_index):
"""Get vf state {True/False}
# Do not store port details, as if they're used for processing
# notifications there is no guarantee the notifications are
# processed in the same order as the relevant API requests.
- self.agent.updated_devices.add(port['mac_address'])
- LOG.debug("port_update RPC received for port: %s", port['id'])
+ mac = port['mac_address']
+ pci_slot = None
+ if port.get('binding:profile'):
+ pci_slot = port['binding:profile'].get('pci_slot')
+
+ if pci_slot:
+ self.agent.updated_devices.add((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})
+ else:
+ LOG.debug("No PCI Slot for port %(id)s with MAC %(mac)s; "
+ "skipping", {'id': port['id'], 'mac': mac,
+ 'pci_slot': pci_slot})
class SriovNicSwitchAgent(object):
# Stores port update notifications for processing in the main loop
self.updated_devices = set()
+ self.mac_to_port_id_mapping = {}
self.context = context.get_admin_context_without_session()
self.plugin_rpc = agent_rpc.PluginApi(topics.PLUGIN)
def _report_state(self):
try:
- devices = len(self.eswitch_mgr.get_assigned_devices())
+ devices = len(self.eswitch_mgr.get_assigned_devices_info())
self.agent_state.get('configurations')['devices'] = devices
self.state_rpc.report_state(self.context,
self.agent_state)
self.eswitch_mgr.discover_devices(device_mappings, exclude_devices)
def scan_devices(self, registered_devices, updated_devices):
- curr_devices = self.eswitch_mgr.get_assigned_devices()
+ curr_devices = self.eswitch_mgr.get_assigned_devices_info()
device_info = {}
device_info['current'] = curr_devices
device_info['added'] = curr_devices - registered_devices
else:
LOG.info(_LI("No device with MAC %s defined on agent."), device)
- def treat_devices_added_updated(self, devices):
+ 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, devices, self.agent_id)
+ self.context, macs_list, self.agent_id)
except Exception as e:
LOG.debug("Unable to get port details for devices "
- "with MAC address %(devices)s: %(e)s",
- {'devices': devices, 'e': e})
+ "with MAC addresses %(devices)s: %(e)s",
+ {'devices': macs_list, 'e': e})
# resync is needed
return True
if 'port_id' in device_details:
LOG.info(_LI("Port %(device)s updated. Details: %(details)s"),
{'device': device, 'details': device_details})
+ port_id = device_details['port_id']
+ self.mac_to_port_id_mapping[device] = port_id
profile = device_details['profile']
spoofcheck = device_details.get('port_security_enabled', True)
- self.treat_device(device_details['device'],
+ self.treat_device(device,
profile.get('pci_slot'),
device_details['admin_state_up'],
spoofcheck)
def treat_devices_removed(self, devices):
resync = False
for device in devices:
- LOG.info(_LI("Removing device with mac_address %s"), device)
+ mac, pci_slot = device
+ LOG.info(_LI("Removing device with MAC address %(mac)s and "
+ "PCI slot %(pci_slot)s"),
+ {'mac': mac, 'pci_slot': pci_slot})
try:
- pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(device)
- if pci_slot:
+ port_id = self.mac_to_port_id_mapping.get(mac)
+ if port_id:
profile = {'pci_slot': pci_slot}
- port = {'device': device, 'profile': profile}
+ port = {'port_id': port_id,
+ 'device': mac,
+ 'profile': profile}
self.ext_manager.delete_port(self.context, port)
+ del self.mac_to_port_id_mapping[mac]
else:
- LOG.warning(_LW("Failed to find pci slot for device "
- "%(device)s; skipping extension port "
- "cleanup"), device)
-
+ LOG.warning(_LW("port_id to device with MAC "
+ "%s not found"), mac)
dev_details = self.plugin_rpc.update_device_down(self.context,
- device,
+ mac,
self.agent_id,
cfg.CONF.host)
+
except Exception as e:
- LOG.debug("Removing port failed for device %(device)s "
- "due to %(exc)s", {'device': device, 'exc': 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})
resync = True
continue
if dev_details['exists']:
- LOG.info(_LI("Port %s updated."), device)
+ LOG.info(_LI("Port with MAC %(mac)s and PCI slot "
+ "%(pci_slot)s updated."),
+ {'mac': mac, 'pci_slot': pci_slot})
else:
- LOG.debug("Device %s not defined on plugin", device)
+ LOG.debug("Device with MAC %(mac)s and PCI slot "
+ "%(pci_slot)s not defined on plugin",
+ {'mac': mac, 'pci_slot': pci_slot})
return resync
def daemon_loop(self):
self.qos_driver.initialize()
self.qos_driver.eswitch_mgr = mock.Mock()
self.qos_driver.eswitch_mgr.set_device_max_rate = mock.Mock()
+ self.qos_driver.eswitch_mgr.clear_max_rate = mock.Mock()
self.max_rate_mock = self.qos_driver.eswitch_mgr.set_device_max_rate
+ self.clear_max_rate_mock = self.qos_driver.eswitch_mgr.clear_max_rate
self.rule = self._create_bw_limit_rule_obj()
self.qos_policy = self._create_qos_policy_obj([self.rule])
self.port = self._create_fake_port()
def test_delete_rules(self):
self.qos_driver.delete(self.port, self.qos_policy)
- self.max_rate_mock.assert_called_once_with(
- self.ASSIGNED_MAC, self.PCI_SLOT, 0)
+ self.clear_max_rate_mock.assert_called_once_with(self.PCI_SLOT)
def test__set_vf_max_rate_captures_sriov_failure(self):
self.max_rate_mock.side_effect = exceptions.SriovNicError()
self.eswitch_mgr = esm.ESwitchManager()
self.eswitch_mgr.discover_devices(device_mappings, None)
- def test_get_assigned_devices(self):
+ def test_get_assigned_devices_info(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
- "eswitch_manager.EmbSwitch.get_assigned_devices",
- return_value=[self.ASSIGNED_MAC]):
- result = self.eswitch_mgr.get_assigned_devices()
- self.assertEqual(set([self.ASSIGNED_MAC]), result)
+ "eswitch_manager.EmbSwitch.get_assigned_devices_info",
+ return_value=[(self.ASSIGNED_MAC, self.PCI_SLOT)]):
+ result = self.eswitch_mgr.get_assigned_devices_info()
+ self.assertIn(self.ASSIGNED_MAC, list(result)[0])
+ self.assertIn(self.PCI_SLOT, list(result)[0])
def test_get_device_status_true(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[ASSIGNED_MAC])
+ return_value={})
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
- "eswitch_manager.PciOsWrapper.is_assigned_vf",
- return_value=True)
- def test_get_pci_slot_by_existing_mac(self, *args):
- pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(self.ASSIGNED_MAC)
- self.assertIsNotNone(pci_slot)
+ "eswitch_manager.EmbSwitch.set_device_max_rate")
+ def test_clear_max_rate_existing_pci_slot(self, max_rate_mock, *args):
+ self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
+ max_rate_mock.assert_called_once_with(self.PCI_SLOT, 0)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[ASSIGNED_MAC])
+ return_value={0: ASSIGNED_MAC})
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
- "eswitch_manager.PciOsWrapper.is_assigned_vf",
- return_value=True)
- def test_get_pci_slot_by_not_existing_mac(self, *args):
- pci_slot = self.eswitch_mgr.get_pci_slot_by_mac(self.WRONG_MAC)
- self.assertIsNone(pci_slot)
+ "eswitch_manager.EmbSwitch.set_device_max_rate")
+ def test_clear_max_rate_exist_and_assigned_pci(
+ self, max_rate_mock, *args):
+ self.eswitch_mgr.clear_max_rate(self.PCI_SLOT)
+ self.assertFalse(max_rate_mock.called)
+
+ @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
+ "eswitch_manager.EmbSwitch.set_device_max_rate")
+ def test_clear_max_rate_nonexisting_pci_slot(self, max_rate_mock):
+ self.eswitch_mgr.clear_max_rate(self.WRONG_PCI)
+ self.assertFalse(max_rate_mock.called)
class TestEmbSwitch(base.BaseTestCase):
SCANNED_DEVICES = [('0000:06:00.1', 0),
('0000:06:00.2', 1),
('0000:06:00.3', 2)]
+ VF_TO_MAC_MAPPING = {0: '00:00:00:00:00:11',
+ 1: '00:00:00:00:00:22',
+ 2: '00:00:00:00:00:33'}
+ EXPECTED_MAC_TO_PCI = {
+ '00:00:00:00:00:11': '0000:06:00.1',
+ '00:00:00:00:00:22': '0000:06:00.2',
+ '00:00:00:00:00:33': '0000:06:00.3'}
def setUp(self):
super(TestEmbSwitch, self).setUp()
self.emb_switch = esm.EmbSwitch(self.PHYS_NET, self.DEV_NAME,
exclude_devices)
- def test_get_assigned_devices(self):
+ @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.PHYS_NET, self.DEV_NAME, ())
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[self.ASSIGNED_MAC]),\
+ return_value={0: self.ASSIGNED_MAC}),\
+ mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
+ "eswitch_manager.PciOsWrapper.is_assigned_vf",
+ 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])
+
+ @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.PHYS_NET, 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",
return_value=True):
- result = self.emb_switch.get_assigned_devices()
- self.assertEqual([self.ASSIGNED_MAC], result)
+ devices_info = 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)
def test_get_assigned_devices_empty(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf",
return_value=False):
- result = self.emb_switch.get_assigned_devices()
+ result = self.emb_switch.get_assigned_devices_info()
self.assertFalse(result)
def test_get_device_state_ok(self):
def test_get_pci_device(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[self.ASSIGNED_MAC]),\
+ return_value={0: self.ASSIGNED_MAC}),\
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf",
return_value=True):
"_as_root") as mock_as_root:
mock_as_root.return_value = self.VF_LINK_SHOW
result = self.pci_wrapper.get_assigned_macs([self.VF_INDEX])
- self.assertEqual([self.MAC_MAPPING[self.VF_INDEX]], result)
+ self.assertEqual(
+ {self.VF_INDEX: self.MAC_MAPPING[self.VF_INDEX]}, result)
def test_get_assigned_macs_fail(self):
with mock.patch.object(self.pci_wrapper,
import mock
from oslo_config import cfg
+from oslo_utils import uuidutils
from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa
from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent
from neutron.tests import base
DEVICE_MAC = '11:22:33:44:55:66'
+PCI_SLOT = "0000:06:00.1"
class TestSriovAgent(base.BaseTestCase):
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[DEVICE_MAC])
+ return_value=[(DEVICE_MAC, PCI_SLOT)])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf",
return_value=True)
def test_treat_devices_removed_with_existed_device(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
- devices = [DEVICE_MAC]
+ devices = [(DEVICE_MAC, PCI_SLOT)]
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.return_value = {'device': DEVICE_MAC,
'exists': True}
- with mock.patch.object(sriov_nic_agent.LOG,
- 'info') as log:
- resync = agent.treat_devices_removed(devices)
- self.assertEqual(2, log.call_count)
- self.assertFalse(resync)
- self.assertTrue(fn_udd.called)
+ resync = agent.treat_devices_removed(devices)
+ self.assertFalse(resync)
+ self.assertTrue(fn_udd.called)
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[DEVICE_MAC])
+ return_value=[(DEVICE_MAC, PCI_SLOT)])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf",
return_value=True)
def test_treat_devices_removed_with_not_existed_device(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
- devices = [DEVICE_MAC]
+ devices = [(DEVICE_MAC, PCI_SLOT)]
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.return_value = {'device': DEVICE_MAC,
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.get_assigned_macs",
- return_value=[DEVICE_MAC])
+ return_value=[(DEVICE_MAC, PCI_SLOT)])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.is_assigned_vf",
return_value=True)
def test_treat_devices_removed_failed(self, *args):
agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0)
- devices = [DEVICE_MAC]
+ devices = [(DEVICE_MAC, PCI_SLOT)]
with mock.patch.object(agent.plugin_rpc,
"update_device_down") as fn_udd:
fn_udd.side_effect = Exception()
def mock_scan_devices(self, expected, mock_current,
registered_devices, updated_devices):
self.agent.eswitch_mgr = mock.Mock()
- self.agent.eswitch_mgr.get_assigned_devices.return_value = mock_current
+ self.agent.eswitch_mgr.get_assigned_devices_info.return_value = (
+ mock_current)
results = self.agent.scan_devices(registered_devices, updated_devices)
self.assertEqual(expected, results)
self.assertFalse(resync_needed)
self.assertFalse(agent.plugin_rpc.update_device_up.called)
+
+
+class FakeAgent(object):
+ def __init__(self):
+ self.updated_devices = set()
+
+
+class TestSriovNicSwitchRpcCallbacks(base.BaseTestCase):
+
+ def setUp(self):
+ super(TestSriovNicSwitchRpcCallbacks, self).setUp()
+ self.context = object()
+ self.agent = FakeAgent()
+ sg_agent = object()
+ self.sriov_rpc_callback = sriov_nic_agent.SriovNicSwitchRpcCallbacks(
+ self.context, self.agent, sg_agent)
+
+ def _create_fake_port(self):
+ return {'id': uuidutils.generate_uuid(),
+ 'binding:profile': {'pci_slot': PCI_SLOT},
+ 'mac_address': DEVICE_MAC}
+
+ def test_port_update_with_pci_slot(self):
+ 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)
+
+ def test_port_update_without_pci_slot(self):
+ port = self._create_fake_port()
+ port['binding:profile'] = None
+ kwargs = {'context': self.context, 'port': port}
+ self.sriov_rpc_callback.port_update(**kwargs)
+ self.assertEqual(set(), self.agent.updated_devices)