diff --git a/neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py b/neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py index 251fa76997c..163165de40d 100644 --- a/neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py +++ b/neutron/conf/plugins/ml2/drivers/mech_sriov/agent_common.py @@ -43,6 +43,35 @@ sriov_nic_opts = [ "network_device. The network_device in the mapping " "should appear in the physical_device_mappings " "list.")), + cfg.ListOpt('resource_provider_bandwidths', + default=[], + help=_("Comma-separated list of " + ":: tuples, " + "showing the available bandwidth for the given device " + "in the given direction. The direction is meant from " + "VM perspective. Bandwidth is measured in kilobits per " + "second (kbps). The device must appear in " + "physical_device_mappings as the value. But not all " + "devices in physical_device_mappings must be listed " + "here. For a device not listed here we neither create " + "a resource provider in placement nor report " + "inventories against. An omitted direction means we do " + "not report an inventory for the corresponding " + "class.")), + cfg.DictOpt('resource_provider_inventory_defaults', + default={'allocation_ratio': 1.0, + 'min_unit': 1, + 'step_size': 1, + 'reserved': 0}, + help=_("Key:value pairs to specify defaults used " + "while reporting resource provider inventories. " + "Possible keys with their types: " + "allocation_ratio:float, " + "max_unit:int, min_unit:int, " + "reserved:int, step_size:int, " + "See also: " + "https://developer.openstack.org/api-ref/placement/" + "#update-resource-provider-inventories")), ] 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 6d9e3be6894..2e5dea368be 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 @@ -24,6 +24,7 @@ from neutron_lib.agent import topics from neutron_lib.api.definitions import portbindings from neutron_lib import constants as n_constants from neutron_lib import context +from neutron_lib.placement import utils as place_utils from neutron_lib.utils import helpers from oslo_config import cfg from oslo_log import log as logging @@ -39,7 +40,9 @@ from neutron.agent import securitygroups_rpc as agent_sg_rpc from neutron.api.rpc.callbacks import resources from neutron.api.rpc.handlers import securitygroups_rpc as sg_rpc from neutron.common import config as common_config +from neutron.common import constants as c_const from neutron.common import profiler as setup_profiler +from neutron.common import utils as n_utils from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config from neutron.plugins.ml2.drivers.mech_sriov.agent.common \ import exceptions as exc @@ -109,7 +112,7 @@ class SriovNicSwitchRpcCallbacks(sg_rpc.SecurityGroupAgentRpcCallbackMixin): @profiler.trace_cls("rpc") class SriovNicSwitchAgent(object): def __init__(self, physical_devices_mappings, exclude_devices, - polling_interval): + polling_interval, rp_bandwidths, rp_inventory_defaults): self.polling_interval = polling_interval self.network_ports = collections.defaultdict(list) @@ -132,6 +135,8 @@ class SriovNicSwitchAgent(object): self.connection) configurations = {'device_mappings': physical_devices_mappings, + c_const.RP_BANDWIDTHS: rp_bandwidths, + c_const.RP_INVENTORY_DEFAULTS: rp_inventory_defaults, 'extensions': self.ext_manager.names()} # TODO(mangelajo): optimize resource_versions (see ovs agent) @@ -427,21 +432,29 @@ class SriovNicAgentConfigParser(object): cfg.CONF.SRIOV_NIC.physical_device_mappings, unique_keys=False) self.exclude_devices = config.parse_exclude_devices( cfg.CONF.SRIOV_NIC.exclude_devices) + self.rp_bandwidths = place_utils.parse_rp_bandwidths( + cfg.CONF.SRIOV_NIC.resource_provider_bandwidths) + self.rp_inventory_defaults = place_utils.parse_rp_inventory_defaults( + cfg.CONF.SRIOV_NIC.resource_provider_inventory_defaults) self._validate() def _validate(self): """Validate configuration. Validate that network_device in excluded_device - exists in device mappings + exists in device mappings. + Validate that network_device in resource_provider_bandwidths + exists in device mappings. """ 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 " - "physical_device_mappings") % {'dev_name': - dev_name}) + raise ValueError(_( + "Invalid exclude_devices: " + "Device name %(dev_name)s is missing from " + "physical_device_mappings") % {'dev_name': dev_name}) + n_utils.validate_rp_bandwidth(self.rp_bandwidths, dev_net_set) def main(): @@ -453,6 +466,8 @@ def main(): config_parser.parse() device_mappings = config_parser.device_mappings exclude_devices = config_parser.exclude_devices + rp_bandwidths = config_parser.rp_bandwidths + rp_inventory_defaults = config_parser.rp_inventory_defaults except ValueError: LOG.exception("Failed on Agent configuration parse. " @@ -465,7 +480,9 @@ def main(): try: agent = SriovNicSwitchAgent(device_mappings, exclude_devices, - polling_interval) + polling_interval, + rp_bandwidths, + rp_inventory_defaults) except exc.SriovNicError: LOG.exception("Agent Initialization Failed") raise SystemExit(1) diff --git a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py index 10cc9fc27b5..2b65f0823b3 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py +++ b/neutron/tests/unit/plugins/ml2/drivers/mech_sriov/agent/test_sriov_nic_agent.py @@ -21,6 +21,7 @@ from oslo_utils import uuidutils from neutron.agent.l2 import l2_agent_extensions_manager as l2_ext_manager from neutron.agent import rpc as agent_rpc +from neutron.common import constants as c_const from neutron.plugins.ml2.drivers.mech_sriov.agent.common import config # noqa from neutron.plugins.ml2.drivers.mech_sriov.agent.common import exceptions from neutron.plugins.ml2.drivers.mech_sriov.agent import sriov_nic_agent @@ -53,7 +54,7 @@ class TestSriovAgent(base.BaseTestCase): 'FixedIntervalLoopingCall', new=MockFixedIntervalLoopingCall) - self.agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) + self.agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}) @mock.patch("neutron.plugins.ml2.drivers.mech_sriov.agent.eswitch_manager" ".ESwitchManager.get_assigned_devices_info", return_value=set()) @@ -81,7 +82,7 @@ class TestSriovAgent(base.BaseTestCase): "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) + agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: @@ -98,7 +99,7 @@ class TestSriovAgent(base.BaseTestCase): "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) + agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: @@ -118,7 +119,7 @@ class TestSriovAgent(base.BaseTestCase): "eswitch_manager.PciOsWrapper.is_assigned_vf", return_value=True) def test_treat_devices_removed_failed(self, *args): - agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) + agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}) devices = [(DEVICE_MAC, PCI_SLOT)] with mock.patch.object(agent.plugin_rpc, "update_device_down") as fn_udd: @@ -532,6 +533,38 @@ class TestSriovAgent(base.BaseTestCase): cleaned_port_id = self.agent._clean_network_ports(mac_slot_2) self.assertEqual({}, self.agent.network_ports) + def test_configurations_has_rp_bandwidth(self): + rp_bandwidth = {'ens7': {'egress': 10000, 'ingress': 10000}} + agent = sriov_nic_agent.SriovNicSwitchAgent( + {}, {}, 0, rp_bandwidth, {}) + self.assertIn(c_const.RP_BANDWIDTHS, + agent.agent_state['configurations']) + + rp_bandwidths = agent.agent_state['configurations'][ + c_const.RP_BANDWIDTHS] + self.assertEqual(rp_bandwidth['ens7'], rp_bandwidths['ens7']) + + def test_configurations_has_rp_default_inventory(self): + rp_inventory_values = { + 'allocation_ratio': 1.0, + 'min_unit': 1, + 'step_size': 1, + 'reserved': 0 + } + agent = sriov_nic_agent.SriovNicSwitchAgent( + {}, {}, 0, {}, rp_inventory_values) + self.assertIn(c_const.RP_INVENTORY_DEFAULTS, + agent.agent_state['configurations']) + + rp_inv_defaults = agent.agent_state['configurations'][ + c_const.RP_INVENTORY_DEFAULTS] + self.assertListEqual( + sorted(list(rp_inventory_values)), + sorted(list(rp_inv_defaults.keys()))) + for inv_key, inv_value in rp_inventory_values.items(): + self.assertEqual(inv_value, + rp_inv_defaults[inv_key]) + class FakeAgent(object): def __init__(self): @@ -605,9 +638,22 @@ class TestSRIOVAgentExtensionConfig(base.BaseTestCase): def test_report_loaded_extension(self, *args): with mock.patch.object(agent_rpc.PluginReportStateAPI, 'report_state') as mock_report_state: - agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0) + agent = sriov_nic_agent.SriovNicSwitchAgent({}, {}, 0, {}, {}) agent._report_state() mock_report_state.assert_called_with( agent.context, agent.agent_state) self.assertEqual( ['qos'], agent.agent_state['configurations']['extensions']) + + +class TestSriovNicAgentConfigParser(base.BaseTestCase): + + def test__validate_rp_in_dev_mappings(self): + with mock.patch.object( + cfg.CONF.SRIOV_NIC, 'physical_device_mappings', + new=[]), \ + mock.patch.object( + cfg.CONF.SRIOV_NIC, 'resource_provider_bandwidths', + new=['no_such_dev_in_dev_mappings:1:1']): + parser = sriov_nic_agent.SriovNicAgentConfigParser() + self.assertRaises(ValueError, parser.parse) diff --git a/releasenotes/notes/bandwidth-config-sriov-bd8ff8b4d84c8792.yaml b/releasenotes/notes/bandwidth-config-sriov-bd8ff8b4d84c8792.yaml new file mode 100644 index 00000000000..df6c33f3d34 --- /dev/null +++ b/releasenotes/notes/bandwidth-config-sriov-bd8ff8b4d84c8792.yaml @@ -0,0 +1,12 @@ +--- +features: + - | + New configuration options for neutron-sriov-agent under section + ``[sriov_nic]``: ``resource_provider_bandwidths`` and + ``resource_provider_inventory_defaults``. + The former controls the ``total`` (available bandwidth) field of the + physical network interface resource provider inventories. It defaults + to not creating resource providers in Placement. The latter can be used + to tune the other fields (``allocation_ratio``, ``min_unit``, + ``max_unit``, ``reserved``, ``step_size``) of resource provider + inventories.