diff --git a/nova/objects/resource_provider.py b/nova/objects/resource_provider.py index fb3c96c492cc..6ad75bcb3ff5 100644 --- a/nova/objects/resource_provider.py +++ b/nova/objects/resource_provider.py @@ -644,6 +644,13 @@ def _provider_ids_from_uuid(context, uuid): return ProviderIds(**dict(res)) +@db_api.api_context_manager.writer +def _delete_rp_record(context, _id): + return context.session.query(models.ResourceProvider).\ + filter(models.ResourceProvider.id == _id).\ + delete(synchronize_session=False) + + @base.NovaObjectRegistry.register_if(False) class ResourceProvider(base.NovaObject, base.NovaTimestampObject): SETTABLE_FIELDS = ('name', 'parent_provider_uuid') @@ -841,11 +848,14 @@ class ResourceProvider(base.NovaObject, base.NovaTimestampObject): RPT_model = models.ResourceProviderTrait context.session.query(RPT_model).\ filter(RPT_model.resource_provider_id == _id).delete() + # set root_provider_id to null to make deletion possible + context.session.query(models.ResourceProvider).\ + filter(models.ResourceProvider.id == _id, + models.ResourceProvider.root_provider_id == _id).\ + update({'root_provider_id': None}) # Now delete the RP record try: - result = context.session.query(models.ResourceProvider).\ - filter(models.ResourceProvider.id == _id).\ - delete(synchronize_session=False) + result = _delete_rp_record(context, _id) except sqla_exc.IntegrityError: # NOTE(jaypipes): Another thread snuck in and parented this # resource provider in between the above check for diff --git a/nova/tests/unit/objects/test_resource_provider.py b/nova/tests/unit/objects/test_resource_provider.py index 674124dbbac0..acc8df7eee9d 100644 --- a/nova/tests/unit/objects/test_resource_provider.py +++ b/nova/tests/unit/objects/test_resource_provider.py @@ -17,6 +17,7 @@ from oslo_utils import timeutils import nova from nova import context +from nova.db.sqlalchemy import api_models as models from nova import exception from nova.objects import fields from nova.objects import resource_provider @@ -219,6 +220,29 @@ class TestResourceProvider(test_objects._LocalTest): resource_provider.ResourceProvider.get_by_uuid, self.context, uuids.rp) + def test_destroy(self): + + def emulate_rp_mysql_delete(func): + def wrapped(context, _id): + rp = context.session.query( + models.ResourceProvider).\ + filter( + models.ResourceProvider.id == _id).first() + self.assertIsNone(rp.root_provider_id) + return func(context, _id) + return wrapped + + emulated = emulate_rp_mysql_delete(resource_provider._delete_rp_record) + + rp = resource_provider.ResourceProvider( + self.context, uuid=_RESOURCE_PROVIDER_UUID, + name=_RESOURCE_PROVIDER_NAME) + rp.create() + + with mock.patch.object( + resource_provider, '_delete_rp_record', emulated): + rp.destroy() + class TestInventoryNoDB(test_objects._LocalTest): USES_DB = False