From 46ddaf4288a1cac44d8afc0525b4ecb3ae2186a3 Mon Sep 17 00:00:00 2001 From: Vladimir Eremin Date: Thu, 17 Mar 2016 19:32:29 +0300 Subject: [PATCH] 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 --- neutron/common/utils.py | 26 ++++++++++++------- .../mech_sriov/agent/eswitch_manager.py | 7 ++--- .../mech_sriov/agent/sriov_nic_agent.py | 6 +++-- neutron/tests/unit/common/test_utils.py | 9 +++++-- .../mech_sriov/agent/common/test_config.py | 11 ++++---- .../mech_sriov/agent/test_eswitch_manager.py | 6 ++--- .../mech_driver/test_mech_sriov_nic_switch.py | 4 +-- ...nics_for_one_physnet-3570aa67a60ce6c4.yaml | 8 ++++++ 8 files changed, 51 insertions(+), 26 deletions(-) create mode 100644 releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml diff --git a/neutron/common/utils.py b/neutron/common/utils.py index 03a953b7c48..0781fa48c93 100644 --- a/neutron/common/utils.py +++ b/neutron/common/utils.py @@ -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 ':' :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 diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py index ea0fac3d0be..201c3c32d94 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/eswitch_manager.py @@ -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) diff --git a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py index 620d9c560a8..c6134baed7d 100644 --- a/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py +++ b/neutron/plugins/ml2/drivers/mech_sriov/agent/sriov_nic_agent.py @@ -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 " diff --git a/neutron/tests/unit/common/test_utils.py b/neutron/tests/unit/common/test_utils.py index b5dcdf9960e..6e0024a1f11 100644 --- a/neutron/tests/unit/common/test_utils.py +++ b/neutron/tests/unit/common/test_utils.py @@ -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 diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py index a0a913fb4f3..68ac77bb3f1 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/common/test_config.py @@ -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): diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py index bdaf9f3aa9e..b60637494c7 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_eswitch_manager.py @@ -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),\ diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py index 310e8cc393f..211d4bf1ee4 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/mech_driver/test_mech_sriov_nic_switch.py @@ -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, diff --git a/releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml b/releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml new file mode 100644 index 00000000000..c87afadb91d --- /dev/null +++ b/releasenotes/notes/sriov_allow_use_many_nics_for_one_physnet-3570aa67a60ce6c4.yaml @@ -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.