Allow to use several nics for physnet with SR-IOV
Accordind specs and docs, SRIOV_NIC.physical_device_mappings is not limited to be a 1-1 mapping between physnets and NICs. However, implementation requires this. This bugfix unlocks 1-M mappings, so one physnet could be managed by many NICs. * introduced unique_keys in neutron.utils.parse_mappings * SRIOV_NIC.physical_device_mappings is parsed as dict with lists as values with parse_mappings(..., unique_keys=False) DocImpact Change-Id: I07b8682fdfe8389a35893cc662b87c94a00bd4a5 Closes-Bug: #1558626
This commit is contained in:
parent
2644c12155
commit
46ddaf4288
@ -214,12 +214,14 @@ def subprocess_popen(args, stdin=None, stdout=None, stderr=None, shell=False,
|
||||
close_fds=close_fds, env=env)
|
||||
|
||||
|
||||
def parse_mappings(mapping_list, unique_values=True):
|
||||
def parse_mappings(mapping_list, unique_values=True, unique_keys=True):
|
||||
"""Parse a list of mapping strings into a dictionary.
|
||||
|
||||
:param mapping_list: a list of strings of the form '<key>:<value>'
|
||||
:param unique_values: values must be unique if True
|
||||
:returns: a dict mapping keys to values
|
||||
:param unique_keys: keys must be unique if True, else implies that keys
|
||||
and values are not unique
|
||||
:returns: a dict mapping keys to values or to list of values
|
||||
"""
|
||||
mappings = {}
|
||||
for mapping in mapping_list:
|
||||
@ -235,14 +237,20 @@ def parse_mappings(mapping_list, unique_values=True):
|
||||
value = split_result[1].strip()
|
||||
if not value:
|
||||
raise ValueError(_("Missing value in mapping: '%s'") % mapping)
|
||||
if key in mappings:
|
||||
raise ValueError(_("Key %(key)s in mapping: '%(mapping)s' not "
|
||||
"unique") % {'key': key, 'mapping': mapping})
|
||||
if unique_values and value in mappings.values():
|
||||
raise ValueError(_("Value %(value)s in mapping: '%(mapping)s' "
|
||||
"not unique") % {'value': value,
|
||||
if unique_keys:
|
||||
if key in mappings:
|
||||
raise ValueError(_("Key %(key)s in mapping: '%(mapping)s' not "
|
||||
"unique") % {'key': key,
|
||||
'mapping': mapping})
|
||||
mappings[key] = value
|
||||
if unique_values and value in mappings.values():
|
||||
raise ValueError(_("Value %(value)s in mapping: '%(mapping)s' "
|
||||
"not unique") % {'value': value,
|
||||
'mapping': mapping})
|
||||
mappings[key] = value
|
||||
else:
|
||||
mappings.setdefault(key, [])
|
||||
if value not in mappings[key]:
|
||||
mappings[key].append(value)
|
||||
return mappings
|
||||
|
||||
|
||||
|
@ -340,9 +340,10 @@ class ESwitchManager(object):
|
||||
"""
|
||||
if exclude_devices is None:
|
||||
exclude_devices = {}
|
||||
for phys_net, dev_name in six.iteritems(device_mappings):
|
||||
self._create_emb_switch(phys_net, dev_name,
|
||||
exclude_devices.get(dev_name, set()))
|
||||
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()))
|
||||
|
||||
def _create_emb_switch(self, phys_net, dev_name, exclude_devices):
|
||||
embedded_switch = EmbSwitch(phys_net, dev_name, exclude_devices)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
|
||||
import collections
|
||||
import itertools
|
||||
import socket
|
||||
import sys
|
||||
import time
|
||||
@ -408,7 +409,7 @@ class SriovNicAgentConfigParser(object):
|
||||
Parse and validate the consistency in both mappings
|
||||
"""
|
||||
self.device_mappings = n_utils.parse_mappings(
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings)
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False)
|
||||
self.exclude_devices = config.parse_exclude_devices(
|
||||
cfg.CONF.SRIOV_NIC.exclude_devices)
|
||||
self._validate()
|
||||
@ -419,7 +420,8 @@ class SriovNicAgentConfigParser(object):
|
||||
Validate that network_device in excluded_device
|
||||
exists in device mappings
|
||||
"""
|
||||
dev_net_set = set(self.device_mappings.values())
|
||||
dev_net_set = set(itertools.chain.from_iterable(
|
||||
six.itervalues(self.device_mappings)))
|
||||
for dev_name in self.exclude_devices.keys():
|
||||
if dev_name not in dev_net_set:
|
||||
raise ValueError(_("Device name %(dev_name)s is missing from "
|
||||
|
@ -32,8 +32,8 @@ from neutron.tests.common import helpers
|
||||
|
||||
|
||||
class TestParseMappings(base.BaseTestCase):
|
||||
def parse(self, mapping_list, unique_values=True):
|
||||
return utils.parse_mappings(mapping_list, unique_values)
|
||||
def parse(self, mapping_list, unique_values=True, unique_keys=True):
|
||||
return utils.parse_mappings(mapping_list, unique_values, unique_keys)
|
||||
|
||||
def test_parse_mappings_fails_for_missing_separator(self):
|
||||
with testtools.ExpectedException(ValueError):
|
||||
@ -73,6 +73,11 @@ class TestParseMappings(base.BaseTestCase):
|
||||
def test_parse_mappings_succeeds_for_no_mappings(self):
|
||||
self.assertEqual({}, self.parse(['']))
|
||||
|
||||
def test_parse_mappings_succeeds_for_nonuniq_key(self):
|
||||
self.assertEqual({'key': ['val1', 'val2']},
|
||||
self.parse(['key:val1', 'key:val2', 'key:val2'],
|
||||
unique_keys=False))
|
||||
|
||||
|
||||
class TestParseTunnelRangesMixin(object):
|
||||
TUN_MIN = None
|
||||
|
@ -46,8 +46,8 @@ class TestSriovAgentConfig(base.BaseTestCase):
|
||||
|
||||
DEVICE_MAPPING_WITH_SPACES_LIST = ['physnet7 : p7p1',
|
||||
'physnet3 : p3p1 ']
|
||||
DEVICE_MAPPING = {'physnet7': 'p7p1',
|
||||
'physnet3': 'p3p1'}
|
||||
DEVICE_MAPPING = {'physnet7': ['p7p1'],
|
||||
'physnet3': ['p3p1']}
|
||||
|
||||
def test_defaults(self):
|
||||
self.assertEqual(config.DEFAULT_DEVICE_MAPPINGS,
|
||||
@ -62,7 +62,7 @@ class TestSriovAgentConfig(base.BaseTestCase):
|
||||
self.DEVICE_MAPPING_LIST,
|
||||
'SRIOV_NIC')
|
||||
device_mappings = n_utils.parse_mappings(
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings)
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False)
|
||||
self.assertEqual(self.DEVICE_MAPPING, device_mappings)
|
||||
|
||||
def test_device_mappings_with_error(self):
|
||||
@ -70,14 +70,15 @@ class TestSriovAgentConfig(base.BaseTestCase):
|
||||
self.DEVICE_MAPPING_WITH_ERROR_LIST,
|
||||
'SRIOV_NIC')
|
||||
self.assertRaises(ValueError, n_utils.parse_mappings,
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings)
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings,
|
||||
unique_keys=False)
|
||||
|
||||
def test_device_mappings_with_spaces(self):
|
||||
cfg.CONF.set_override('physical_device_mappings',
|
||||
self.DEVICE_MAPPING_WITH_SPACES_LIST,
|
||||
'SRIOV_NIC')
|
||||
device_mappings = n_utils.parse_mappings(
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings)
|
||||
cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False)
|
||||
self.assertEqual(self.DEVICE_MAPPING, device_mappings)
|
||||
|
||||
def test_exclude_devices(self):
|
||||
|
@ -31,7 +31,7 @@ class TestCreateESwitchManager(base.BaseTestCase):
|
||||
('0000:06:00.3', 2)]
|
||||
|
||||
def test_create_eswitch_mgr_fail(self):
|
||||
device_mappings = {'physnet1': 'p6p1'}
|
||||
device_mappings = {'physnet1': ['p6p1']}
|
||||
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
|
||||
"eswitch_manager.PciOsWrapper.scan_vf_devices",
|
||||
side_effect=exc.InvalidDeviceError(
|
||||
@ -45,7 +45,7 @@ class TestCreateESwitchManager(base.BaseTestCase):
|
||||
device_mappings, None)
|
||||
|
||||
def test_create_eswitch_mgr_ok(self):
|
||||
device_mappings = {'physnet1': 'p6p1'}
|
||||
device_mappings = {'physnet1': ['p6p1']}
|
||||
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
|
||||
"eswitch_manager.PciOsWrapper.scan_vf_devices",
|
||||
return_value=self.SCANNED_DEVICES),\
|
||||
@ -68,7 +68,7 @@ class TestESwitchManagerApi(base.BaseTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestESwitchManagerApi, self).setUp()
|
||||
device_mappings = {'physnet1': 'p6p1'}
|
||||
device_mappings = {'physnet1': ['p6p1']}
|
||||
with mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent."
|
||||
"eswitch_manager.PciOsWrapper.scan_vf_devices",
|
||||
return_value=self.SCANNED_DEVICES),\
|
||||
|
@ -61,10 +61,10 @@ class SriovNicSwitchMechanismBaseTestCase(base.AgentMechanismBaseTestCase):
|
||||
AGENT_TYPE = constants.AGENT_TYPE_NIC_SWITCH
|
||||
VLAN_SEGMENTS = base.AgentMechanismVlanTestCase.VLAN_SEGMENTS
|
||||
|
||||
GOOD_MAPPINGS = {'fake_physical_network': 'fake_device'}
|
||||
GOOD_MAPPINGS = {'fake_physical_network': ['fake_device']}
|
||||
GOOD_CONFIGS = {'device_mappings': GOOD_MAPPINGS}
|
||||
|
||||
BAD_MAPPINGS = {'wrong_physical_network': 'wrong_device'}
|
||||
BAD_MAPPINGS = {'wrong_physical_network': ['wrong_device']}
|
||||
BAD_CONFIGS = {'device_mappings': BAD_MAPPINGS}
|
||||
|
||||
AGENTS = [{'alive': True,
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
prelude: >
|
||||
Several NICs per physical network can be used with SR-IOV.
|
||||
fixes:
|
||||
- The 'physical_device_mappings' of sriov_nic configuration now can accept
|
||||
more than one NIC per physical network. For example, if 'physnet2' is
|
||||
connected to enp1s0f0 and enp1s0f1, 'physnet2:enp1s0f0,physnet2:enp1s0f1'
|
||||
will be a valid option.
|
Loading…
x
Reference in New Issue
Block a user