From 0bfac5c7fedece9fe28e0eebf9c7fb535a5ee431 Mon Sep 17 00:00:00 2001 From: Balazs Gibizer Date: Tue, 27 May 2025 16:48:22 +0200 Subject: [PATCH] Return HTTP400 for multi spec pci alias if PCI in Placement PCI in Placement never supported PCI aliases with multiple specs, i.e. when an alias name is used in multiple alias definitions. The code raised ValueError late and without a proper error message. Now PciInvalidAlias with a descriptive message is raised instead. Closes-Bug: #2102038 Change-Id: Id1407a37dc5ddad22d8dbf7d589ed998ffc2804e --- nova/pci/request.py | 18 +++++++++++++++ .../regressions/test_bug_2102038.py | 12 ++++++---- nova/tests/unit/pci/test_request.py | 22 +++++++++++++++++++ 3 files changed, 48 insertions(+), 4 deletions(-) diff --git a/nova/pci/request.py b/nova/pci/request.py index fe12bb50fd..6ef87df551 100644 --- a/nova/pci/request.py +++ b/nova/pci/request.py @@ -121,6 +121,23 @@ _ALIAS_SCHEMA = { } +def _validate_aliases(aliases): + """Checks the parsed aliases for common mistakes and raise easy to parse + error messages + """ + if CONF.filter_scheduler.pci_in_placement: + alias_with_multiple_specs = [ + name for name, spec in aliases.items() if len(spec[1]) > 1] + if alias_with_multiple_specs: + raise exception.PciInvalidAlias( + "The PCI alias(es) %s have multiple specs but " + "[filter_scheduler]pci_in_placement is True. The PCI in " + "Placement feature only supports one spec per alias. You can " + "assign the same resource_class to multiple [pci]device_spec " + "matchers to allow using different devices for the same alias." + % ",".join(alias_with_multiple_specs)) + + def _get_alias_from_config() -> Alias: """Parse and validate PCI aliases from the nova config. @@ -177,6 +194,7 @@ def _get_alias_from_config() -> Alias: except Exception as exc: raise exception.PciInvalidAlias(reason=str(exc)) + _validate_aliases(aliases) return aliases diff --git a/nova/tests/functional/regressions/test_bug_2102038.py b/nova/tests/functional/regressions/test_bug_2102038.py index e867e27c6f..f1f1d13770 100644 --- a/nova/tests/functional/regressions/test_bug_2102038.py +++ b/nova/tests/functional/regressions/test_bug_2102038.py @@ -49,7 +49,11 @@ class MultipleSpecPerAliasWithPCIInPlacementTest( flavor_id=flavor_id, networks=[], ) - # This is bug 2102038 as nova does not handle the internal ValueError - # and therefore returns HTTP 500 instead of returning 400 Bad Request - # with a message pointing to the unsupported alias config. - self.assertEqual(500, exc.response.status_code) + self.assertEqual(400, exc.response.status_code) + self.assertIn( + "The PCI alias(es) a-vf have multiple specs but " + "[filter_scheduler]pci_in_placement is True. The PCI in Placement " + "feature only supports one spec per alias. You can assign the " + "same resource_class to multiple [pci]device_spec matchers to " + "allow using different devices for the same alias.", + exc.response.text) diff --git a/nova/tests/unit/pci/test_request.py b/nova/tests/unit/pci/test_request.py index be9c6be877..fcd14d117c 100644 --- a/nova/tests/unit/pci/test_request.py +++ b/nova/tests/unit/pci/test_request.py @@ -120,6 +120,28 @@ class PciRequestTestCase(test.NoDBTestCase): }]) self.assertEqual(expected_result, result['QuickAssist']) + def test_get_alias_from_config_multispec_rejected_pci_in_placement(self): + _fake_alias = jsonutils.dumps({ + "name": "QuickAssist", + "capability_type": "pci", + "product_id": "4444", + "vendor_id": "8086", + "device_type": "type-PCI", + }) + + self.flags(pci_in_placement=True, group='filter_scheduler') + self.flags(alias=[_fake_alias1, _fake_alias], group='pci') + + ex = self.assertRaises( + exception.PciInvalidAlias, request._get_alias_from_config) + self.assertEqual( + "The PCI alias(es) QuickAssist have multiple specs but " + "[filter_scheduler]pci_in_placement is True. The PCI in Placement " + "feature only supports one spec per alias. You can assign the " + "same resource_class to multiple [pci]device_spec matchers to " + "allow using different devices for the same alias.", + str(ex)) + def _test_get_alias_from_config_invalid(self, alias): self.flags(alias=[alias], group='pci') self.assertRaises(