Allow the other nic to allocate VMs post PCI-PT VM creation.

Let's say a compute node has got 2 network adapters
(em1 and em2) with its correspondent VFs configured.
If you start a VM with a direct-physical binding
it will take one of these NICs.

At that moment, SRIOV agent starts to show
ERROR messages including the "device dictionary"
completely empty.

In consequence, you cannot allocate VMs with VFs
even though there is still another NIC available.

Conflicts:
    neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py

Change-Id: I8bf0dd41f900b69e32fcd416690c089dde7989b9
Closes-Bug: #1616442
(cherry picked from commit 1bcdc299ba)
This commit is contained in:
Manjunath Patil 2016-09-27 20:12:39 +05:30 committed by Brent Eagles
parent d4cc9226b5
commit ea95bf8af8
3 changed files with 126 additions and 6 deletions

View File

@ -62,6 +62,10 @@ class PciOsWrapper(object):
vf_list.append((pci_slot, vf_index))
return vf_list
@classmethod
def pf_device_exists(cls, dev_name):
return os.path.isdir(cls.DEVICE_PATH % dev_name)
@classmethod
def is_assigned_vf(cls, dev_name, vf_index, ip_link_show_output):
"""Check if VF is assigned.
@ -75,12 +79,19 @@ class PciOsWrapper(object):
@param vf_index: vf index
@param ip_link_show_output: 'ip link show' output
"""
if not cls.pf_device_exists(dev_name):
# If the root PCI path does not exist, then the VF cannot
# actually have been allocated and there is no way we can
# manage it.
return False
path = cls.PCI_PATH % (dev_name, vf_index)
try:
ifname_list = os.listdir(path)
except OSError:
# PCI_PATH does not exist means that the DIRECT VF assigend
# PCI_PATH does not exist means that the DIRECT VF assigned
return True
# Note(moshele) kernel < 3.13 doesn't create symbolic link
@ -351,6 +362,26 @@ class ESwitchManager(object):
embedded_switch.set_device_spoofcheck(pci_slot,
enabled)
def _process_emb_switch_map(self, phys_net, dev_name, exclude_devices):
"""Process emb_switch_map
@param phys_net: physical network
@param dev_name: device name
@param exclude_devices: PCI devices to ignore.
"""
emb_switches = self.emb_switches_map.get(phys_net, [])
for switch in emb_switches:
if switch.dev_name == dev_name:
if not PciOsWrapper.pf_device_exists(dev_name):
# If the device is given to the VM as PCI-PT
# then delete the respective emb_switch from map
self.emb_switches_map.get(phys_net).remove(switch)
return
# We don't know about this device at the moment, so add to the map.
if PciOsWrapper.pf_device_exists(dev_name):
self._create_emb_switch(phys_net, dev_name,
exclude_devices.get(dev_name, set()))
def discover_devices(self, device_mappings, exclude_devices):
"""Discover which Virtual functions to manage.
@ -362,8 +393,8 @@ class ESwitchManager(object):
exclude_devices = {}
for phys_net, dev_names in six.iteritems(device_mappings):
for dev_name in dev_names:
self._create_emb_switch(phys_net, dev_name,
exclude_devices.get(dev_name, set()))
self._process_emb_switch_map(phys_net, dev_name,
exclude_devices)
def _create_emb_switch(self, phys_net, dev_name, exclude_devices):
embedded_switch = EmbSwitch(phys_net, dev_name, exclude_devices)

View File

@ -113,6 +113,8 @@ class SriovNicSwitchAgent(object):
self.polling_interval = polling_interval
self.network_ports = collections.defaultdict(list)
self.conf = cfg.CONF
self.device_mappings = physical_devices_mappings
self.exclude_devices = exclude_devices
self.setup_eswitch_mgr(physical_devices_mappings,
exclude_devices)
@ -376,6 +378,8 @@ class SriovNicSwitchAgent(object):
updated_devices_copy = self.updated_devices
self.updated_devices = set()
try:
self.eswitch_mgr.discover_devices(self.device_mappings,
self.exclude_devices)
device_info = self.scan_devices(devices, updated_devices_copy)
if self._device_info_has_changes(device_info):
LOG.debug("Agent loop found changes! %s", device_info)

View File

@ -41,6 +41,9 @@ class TestCreateESwitchManager(base.BaseTestCase):
"eswitch_manager.PciOsWrapper.scan_vf_devices",
side_effect=exc.InvalidDeviceError(
dev_name="p6p1", reason="device" " not found")),\
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",
return_value=True):
@ -55,6 +58,9 @@ class TestCreateESwitchManager(base.BaseTestCase):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.scan_vf_devices",
return_value=self.SCANNED_DEVICES),\
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",
return_value=True):
@ -91,11 +97,36 @@ class TestESwitchManagerApi(base.BaseTestCase):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.scan_vf_devices",
return_value=self.SCANNED_DEVICES), \
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",
return_value=True):
eswitch_mgr.discover_devices(device_mappings, None)
def test_discover_devices_with_device(self):
device_mappings = {'physnet1': ['p6p1', 'p6p2']}
with 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.ESwitchManager._create_emb_switch",
) as emb_switch:
self.eswitch_mgr.discover_devices(device_mappings, None)
self.assertTrue(emb_switch.called)
def test_discover_devices_without_device(self):
device_mappings = {'physnet1': ['p6p1', 'p6p2']}
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.pf_device_exists",
return_value=False), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.ESwitchManager._create_emb_switch",
) as emb_switch:
self.eswitch_mgr.discover_devices(device_mappings, None)
self.assertFalse(emb_switch.called)
def test_get_assigned_devices_info(self):
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.EmbSwitch.get_assigned_devices_info",
@ -248,6 +279,36 @@ class TestESwitchManagerApi(base.BaseTestCase):
'device_mac': self.WRONG_MAC})
self.assertFalse(result)
def test_process_emb_switch_without_device(self):
device_mappings = {'physnet1': ['p6p1', 'p6p2']}
phys_net = 'physnet1'
dev_name = 'p6p1'
self._set_eswitch_manager(self.eswitch_mgr, device_mappings)
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.pf_device_exists",
return_value=False), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.ESwitchManager._create_emb_switch",
) as emb_switch:
self.eswitch_mgr._process_emb_switch_map(phys_net,
dev_name, {})
self.assertFalse(emb_switch.called)
def test_process_emb_switch_with_device(self):
device_mappings = {'physnet1': ['p6p1', 'p6p2']}
phys_net = 'physnet1'
dev_name = 'p6p3'
self._set_eswitch_manager(self.eswitch_mgr, device_mappings)
with 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.ESwitchManager._create_emb_switch",
) as emb_switch:
self.eswitch_mgr._process_emb_switch_map(phys_net,
dev_name, {})
self.assertTrue(emb_switch.called)
def _test_clear_rate(self, rate_type, pci_slot, passed, mac_address):
with mock.patch('neutron.plugins.ml2.drivers.mech_sriov.agent.'
'eswitch_manager.EmbSwitch.set_device_rate') \
@ -257,7 +318,10 @@ class TestESwitchManagerApi(base.BaseTestCase):
return_value=mac_address), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"pci_lib.PciDeviceIPWrapper.link_show",
return_value=''):
return_value=''), \
mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.pf_device_exists",
return_value=True):
self.eswitch_mgr.clear_rate(pci_slot, rate_type)
if passed:
set_rate_mock.assert_called_once_with(pci_slot, rate_type, 0)
@ -557,9 +621,13 @@ class TestPciOsWrapper(base.BaseTestCase):
@mock.patch("os.listdir", side_effect=OSError())
def test_is_assigned_vf_true(self, *args):
self.assertTrue(esm.PciOsWrapper.is_assigned_vf(
self.DEV_NAME, self.VF_INDEX, ''))
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
"eswitch_manager.PciOsWrapper.pf_device_exists",
return_value=True):
self.assertTrue(esm.PciOsWrapper.is_assigned_vf(
self.DEV_NAME, self.VF_INDEX, ''))
@mock.patch("os.path.exists", return_value=True)
@mock.patch("os.listdir", return_value=[DEV_NAME, "eth1"])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.is_macvtap_assigned", return_value=False)
@ -567,6 +635,7 @@ class TestPciOsWrapper(base.BaseTestCase):
self.assertFalse(esm.PciOsWrapper.is_assigned_vf(
self.DEV_NAME, self.VF_INDEX, ''))
@mock.patch("os.path.exists", return_value=True)
@mock.patch("os.listdir", return_value=["eth0", "eth1"])
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.is_macvtap_assigned", return_value=True)
@ -575,6 +644,7 @@ class TestPciOsWrapper(base.BaseTestCase):
esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '')
mock_is_macvtap_assigned.called_with(self.VF_INDEX, "eth0")
@mock.patch("os.path.exists", return_value=True)
@mock.patch("os.listdir", side_effect=OSError())
@mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.pci_lib."
"PciDeviceIPWrapper.is_macvtap_assigned")
@ -582,3 +652,18 @@ class TestPciOsWrapper(base.BaseTestCase):
self, mock_is_macvtap_assigned, *args):
esm.PciOsWrapper.is_assigned_vf(self.DEV_NAME, self.VF_INDEX, '')
self.assertFalse(mock_is_macvtap_assigned.called)
@mock.patch("os.path.exists", return_value=False)
@mock.patch("os.listdir", return_value=["eth0", "eth1"])
def test_is_assigned_vf_pf_disappeared(self, list_dir_mock, *args):
self.assertFalse(esm.PciOsWrapper.is_assigned_vf(
self.DEV_NAME, self.VF_INDEX, ''))
self.assertFalse(list_dir_mock.called)
def test_pf_device_exists_with_no_dir(self):
with mock.patch("os.path.isdir", return_value=False):
self.assertFalse(esm.PciOsWrapper.pf_device_exists('p6p1'))
def test_pf_device_exists_with_dir(self):
with mock.patch("os.path.isdir", return_value=True):
self.assertTrue(esm.PciOsWrapper.pf_device_exists('p6p1'))