APIDefinitionFixture bugfix

Currently APIDefinitionFixture backs up the contents of its target dict
(attributes.RESOURCES and/or an API def's RESOURCE_ATTRIBUTE_MAP) and
on restore replaces the respective target with its backup copy. While
this works in a number of cases, it doesn't take into account "longer"
references to the respective dict. In some cases consumers hold a
reference to an attribute dict longer than the duration of the fixture
in which case their reference is not cleaned up on restore.

This patch address this bug and includes UTs. No release note is
included as this is only an internal behavior change.

Change-Id: Id61ba4e4649d3a73c9397632755f1cd0c38d7ab3
This commit is contained in:
Boden R 2017-05-22 10:46:04 -06:00
parent 67b1ea3a4a
commit 1a98539797
2 changed files with 37 additions and 5 deletions

View File

@ -132,20 +132,22 @@ class APIDefinitionFixture(fixtures.Fixture):
def _setUp(self):
for api_def in self.definitions:
self._orig_attr_maps[api_def.ALIAS] = (
api_def, api_def.RESOURCE_ATTRIBUTE_MAP)
api_def.RESOURCE_ATTRIBUTE_MAP = copy.deepcopy(
api_def.RESOURCE_ATTRIBUTE_MAP)
api_def, {k: copy.deepcopy(v)
for k, v in api_def.RESOURCE_ATTRIBUTE_MAP.items()})
if self.backup_global_resources:
for resource, attrs in attributes.RESOURCES.items():
self._orig_resources[resource] = copy.deepcopy(attrs)
self.addCleanup(self._restore)
def _restore(self):
# clear + repopulate so consumer refs don't change
for alias, def_and_map in self._orig_attr_maps.items():
api_def, attr_map = def_and_map[0], def_and_map[1]
api_def.RESOURCE_ATTRIBUTE_MAP = attr_map
api_def.RESOURCE_ATTRIBUTE_MAP.clear()
api_def.RESOURCE_ATTRIBUTE_MAP.update(attr_map)
if self.backup_global_resources:
attributes.RESOURCES = self._orig_resources
attributes.RESOURCES.clear()
attributes.RESOURCES.update(self._orig_resources)
@classmethod
def all_api_definitions_fixture(cls):

View File

@ -17,6 +17,7 @@ from oslo_db import options
from oslotest import base
from neutron_lib.api import attributes
from neutron_lib.api.definitions import port
from neutron_lib.callbacks import registry
from neutron_lib.db import model_base
from neutron_lib import fixture
@ -92,3 +93,32 @@ class APIDefinitionFixtureTestCase(base.BaseTestCase):
def test_all_api_definitions_fixture_with_global_backup(self):
self._test_all_api_definitions_fixture(global_cleanup=True)
def test_global_resources_reference_updated(self):
resources_ref = attributes.RESOURCES
apis = fixture.APIDefinitionFixture()
apis.setUp()
attributes.RESOURCES['test_resource'] = {}
self.assertIn('test_resource', resources_ref)
attributes.RESOURCES[port.COLLECTION_NAME]['test_port_attr'] = {}
self.assertIn('test_port_attr',
attributes.RESOURCES[port.COLLECTION_NAME])
apis.cleanUp()
self.assertNotIn('test_port_attr',
attributes.RESOURCES[port.COLLECTION_NAME])
self.assertNotIn('test_resource', resources_ref)
def test_api_def_reference_updated(self):
api_def_ref = port.RESOURCE_ATTRIBUTE_MAP
apis = fixture.APIDefinitionFixture()
apis.setUp()
port.RESOURCE_ATTRIBUTE_MAP[port.COLLECTION_NAME]['test_attr'] = {}
self.assertIn('test_attr', api_def_ref[port.COLLECTION_NAME])
apis.cleanUp()
self.assertNotIn('test_attr',
port.RESOURCE_ATTRIBUTE_MAP[port.COLLECTION_NAME])
self.assertNotIn('test_attr', api_def_ref[port.COLLECTION_NAME])