From 36bb16b941438d2802c86cc6f3ae760b484e2165 Mon Sep 17 00:00:00 2001 From: Boden R Date: Wed, 22 Nov 2017 10:46:44 -0700 Subject: [PATCH] ut updates for extending sub-resources When extending an existing sub-resource definition, the SUB_RESOURCE_ATTRIBUTE_MAP must define the extended resources/attributes under the 'parameters' object. This instructs the API internals to update the existing sub-resource's attribute map rather than just overwrite it. For internals see the logic in the extend_resources() method in neutron.api.extensions [1]. For example the qos_bw_limit_direction in neutron uses this approach for its BANDWIDTH_LIMIT_RULES definition (also see related lib commit I823f567ddf9c895eb3cf94e95ae34a382d67f51d). This patch adds that support to the base api def test case and also updates the contributor internals to document such. No release note included since nothing changed from a consumption perspective. [1] https://github.com/openstack/neutron/blob/master/neutron/api/extensions.py#L350 Change-Id: Ib91004490dc3ca71b7caafe5cc3cb07417be1e4c --- doc/source/contributor/api_attributes.rst | 18 ++++++++++ neutron_lib/api/definitions/_dummy.py | 5 ++- .../tests/unit/api/definitions/base.py | 35 +++++++++++++------ 3 files changed, 46 insertions(+), 12 deletions(-) diff --git a/doc/source/contributor/api_attributes.rst b/doc/source/contributor/api_attributes.rst index 89bf20257..ebddefa67 100644 --- a/doc/source/contributor/api_attributes.rst +++ b/doc/source/contributor/api_attributes.rst @@ -89,3 +89,21 @@ The following are the defined keys for attribute maps: ``enforce_policy`` the attribute is actively part of the policy enforcing mechanism, ie: there might be rules which refer to this attribute ``primary_key`` Mark the attribute as a unique key. ====================== ====== + +When extending existing sub-resources, the sub-attribute map must define all +extension attributes under the ``parameters`` object. This instructs the API +internals to add the attributes to the existing sub-resource rather than +overwrite its existing definition. For example: + +.. code-block:: python + + SUB_RESOURCE_ATTRIBUTE_MAP = { + 'existing_subresource_to_extend': { + 'parameters': { + 'new_attr1': { + 'allow_post': False, + # etc.. + } + } + } + } diff --git a/neutron_lib/api/definitions/_dummy.py b/neutron_lib/api/definitions/_dummy.py index e552dc99a..db82e2b7a 100644 --- a/neutron_lib/api/definitions/_dummy.py +++ b/neutron_lib/api/definitions/_dummy.py @@ -76,7 +76,10 @@ RESOURCE_ATTRIBUTE_MAP = { # The subresource attribute map for the extension. It adds child resources # to main extension's resource. The subresource map must have a parent and # a parameters entry. If an extension does not need such a map, None can -# be specified (mandatory). For example: +# be specified (mandatory). +# Note that if an existing sub-resource is being extended, the +# existing resources to extend the new extension attributes must be +# defined under the 'parameters' key. SUB_RESOURCE_ATTRIBUTE_MAP = { 'subfoo': { 'parent': { diff --git a/neutron_lib/tests/unit/api/definitions/base.py b/neutron_lib/tests/unit/api/definitions/base.py index 2a0ef1a55..5897581d2 100644 --- a/neutron_lib/tests/unit/api/definitions/base.py +++ b/neutron_lib/tests/unit/api/definitions/base.py @@ -132,23 +132,36 @@ class DefinitionBaseTestCase(test_base.BaseTestCase): resource[attribute], keyword, value) + def _assert_subresource(self, subresource): + self.assertIn( + self.subresource_map[subresource]['parent']['collection_name'], + base.KNOWN_RESOURCES + self.extension_resources, + 'Sub-resource parent is unknown, check for typos.') + self.assertIn('member_name', + self.subresource_map[subresource]['parent'], + 'Incorrect parent definition, check for typos.') + self.assertParams(self.subresource_map[subresource]['parameters']) + def test_subresource_map(self): if not self.subresource_map: self.skipTest('API extension has no subresource map.') for subresource in self.subresource_map: self.assertIn( - subresource, self.extension_subresources, + subresource, + self.extension_subresources + base.KNOWN_RESOURCES, 'Sub-resource is unknown, check for typos.') - for attribute in self.subresource_map[subresource]: - self.assertIn(attribute, ('parent', 'parameters')) - self.assertIn( - self.subresource_map[subresource]['parent']['collection_name'], - base.KNOWN_RESOURCES + self.extension_resources, - 'Sub-resource parent is unknown, check for typos.') - self.assertIn('member_name', - self.subresource_map[subresource]['parent'], - 'Incorrect parent definition, check for typos.') - self.assertParams(self.subresource_map[subresource]['parameters']) + sub_attrmap = self.subresource_map[subresource] + if 'parent' in sub_attrmap: + self.assertEqual(2, len(sub_attrmap.keys())) + self.assertIn('parent', sub_attrmap) + self.assertIn('parameters', sub_attrmap) + self._assert_subresource(subresource) + else: + self.assertEqual( + ['parameters'], [p for p in sub_attrmap.keys()], + 'When extending sub-resources only use the parameters ' + 'keyword') + self.assertParams(sub_attrmap['parameters']) def test_action_map(self): self.assertIsInstance(self.action_map, dict)