Implement ResourceProvider.destroy()

Destroying a resource provider first checks to see if there are
existing allocations associated with the provider. If there are
a ResourceProviderInUse exception is raised. If there are not,
the resource provider and associated inventories are deleted
from the database.

Change-Id: Icf0874e00bbee23aa805349e08273be09160c0de
Partially-Implements: blueprint generic-resource-pools
This commit is contained in:
Chris Dent 2016-07-05 23:34:04 +00:00
parent eb7cdb053a
commit 83bf4bb312
4 changed files with 112 additions and 41 deletions

View File

@ -2094,6 +2094,10 @@ class ConcurrentUpdateDetected(NovaException):
"Please retry your update") "Please retry your update")
class ResourceProviderInUse(NovaException):
msg_fmt = _("Resource provider has allocations.")
class UnsupportedPointerModelRequested(Invalid): class UnsupportedPointerModelRequested(Invalid):
msg_fmt = _("Pointer model '%(model)s' requested is not supported by " msg_fmt = _("Pointer model '%(model)s' requested is not supported by "
"host.") "host.")

View File

@ -33,6 +33,23 @@ def _create_rp_in_db(context, updates):
return db_rp return db_rp
@db_api.api_context_manager.writer
def _delete_rp_from_db(context, _id):
# Don't delete the resource provider if it has allocations.
rp_allocations = context.session.query(models.Allocation).\
filter(models.Allocation.resource_provider_id == _id).\
count()
if rp_allocations:
raise exception.ResourceProviderInUse()
# Delete any inventory associated with the resource provider
context.session.query(models.Inventory).\
filter(models.Inventory.resource_provider_id == _id).delete()
result = context.session.query(models.ResourceProvider).\
filter(models.ResourceProvider.id == _id).delete()
if not result:
raise exception.NotFound()
@db_api.api_context_manager.writer @db_api.api_context_manager.writer
def _update_rp_in_db(context, id, updates): def _update_rp_in_db(context, id, updates):
db_rp = context.session.query(models.ResourceProvider).filter_by( db_rp = context.session.query(models.ResourceProvider).filter_by(
@ -248,7 +265,8 @@ def _set_inventory(context, rp, inv_list):
@base.NovaObjectRegistry.register @base.NovaObjectRegistry.register
class ResourceProvider(base.NovaObject): class ResourceProvider(base.NovaObject):
# Version 1.0: Initial version # Version 1.0: Initial version
VERSION = '1.0' # Version 1.1: Add destroy()
VERSION = '1.1'
fields = { fields = {
'id': fields.IntegerField(read_only=True), 'id': fields.IntegerField(read_only=True),
@ -272,6 +290,10 @@ class ResourceProvider(base.NovaObject):
db_rp = self._create_in_db(self._context, updates) db_rp = self._create_in_db(self._context, updates)
self._from_db_object(self._context, self, db_rp) self._from_db_object(self._context, self, db_rp)
@base.remotable
def destroy(self):
self._delete(self._context, self.id)
@base.remotable @base.remotable
def save(self): def save(self):
updates = self.obj_get_changes() updates = self.obj_get_changes()
@ -322,6 +344,10 @@ class ResourceProvider(base.NovaObject):
def _create_in_db(context, updates): def _create_in_db(context, updates):
return _create_rp_in_db(context, updates) return _create_rp_in_db(context, updates)
@staticmethod
def _delete(context, id):
_delete_rp_from_db(context, id)
@staticmethod @staticmethod
def _update_in_db(context, id, updates): def _update_in_db(context, id, updates):
return _update_rp_in_db(context, id, updates) return _update_rp_in_db(context, id, updates)

View File

@ -51,6 +51,20 @@ class ResourceProviderBaseCase(test.NoDBTestCase):
self.useFixture(fixtures.Database(database='api')) self.useFixture(fixtures.Database(database='api'))
self.context = context.RequestContext('fake-user', 'fake-project') self.context = context.RequestContext('fake-user', 'fake-project')
def _make_allocation(self, rp_uuid=None):
rp_uuid = rp_uuid or uuidsentinel.allocation_resource_provider
db_rp = objects.ResourceProvider(
context=self.context,
uuid=rp_uuid,
name=rp_uuid)
db_rp.create()
updates = dict(DISK_ALLOCATION,
resource_class_id=RESOURCE_CLASS_ID,
resource_provider_id=db_rp.id)
db_allocation = objects.Allocation._create_in_db(self.context,
updates)
return db_rp, db_allocation
class ResourceProviderTestCase(ResourceProviderBaseCase): class ResourceProviderTestCase(ResourceProviderBaseCase):
"""Test resource-provider objects' lifecycles.""" """Test resource-provider objects' lifecycles."""
@ -98,6 +112,47 @@ class ResourceProviderTestCase(ResourceProviderBaseCase):
) )
self.assertEqual('new-name', retrieved_resource_provider.name) self.assertEqual('new-name', retrieved_resource_provider.name)
def test_destroy_resource_provider(self):
created_resource_provider = objects.ResourceProvider(
context=self.context,
uuid=uuidsentinel.fake_resource_provider,
name=uuidsentinel.fake_resource_name,
)
created_resource_provider.create()
created_resource_provider.destroy()
self.assertRaises(exception.NotFound,
objects.ResourceProvider.get_by_uuid,
self.context,
uuidsentinel.fake_resource_provider)
self.assertRaises(exception.NotFound,
created_resource_provider.destroy)
def test_destroy_allocated_resource_provider_fails(self):
rp, allocation = self._make_allocation()
self.assertRaises(exception.ResourceProviderInUse,
rp.destroy)
def test_destroy_resource_provider_destroy_inventory(self):
resource_provider = objects.ResourceProvider(
context=self.context,
uuid=uuidsentinel.fake_resource_provider,
name=uuidsentinel.fake_resource_name,
)
resource_provider.create()
disk_inventory = objects.Inventory(
context=self.context,
resource_provider=resource_provider,
**DISK_INVENTORY
)
disk_inventory.create()
inventories = objects.InventoryList.get_all_by_resource_provider_uuid(
self.context, resource_provider.uuid)
self.assertEqual(1, len(inventories))
resource_provider.destroy()
inventories = objects.InventoryList.get_all_by_resource_provider_uuid(
self.context, resource_provider.uuid)
self.assertEqual(0, len(inventories))
def test_create_inventory_with_uncreated_provider(self): def test_create_inventory_with_uncreated_provider(self):
resource_provider = objects.ResourceProvider( resource_provider = objects.ResourceProvider(
context=self.context, context=self.context,
@ -338,20 +393,6 @@ class ResourceProviderListTestCase(test.NoDBTestCase):
class TestAllocation(ResourceProviderBaseCase): class TestAllocation(ResourceProviderBaseCase):
def _make_allocation(self, rp_uuid=None):
rp_uuid = rp_uuid or uuidsentinel.allocation_resource_provider
db_rp = objects.ResourceProvider(
context=self.context,
uuid=rp_uuid,
name=rp_uuid)
db_rp.create()
updates = dict(DISK_ALLOCATION,
resource_class_id=RESOURCE_CLASS_ID,
resource_provider_id=db_rp.id)
db_allocation = objects.Allocation._create_in_db(self.context,
updates)
return db_rp, db_allocation
def test_create_list_and_delete_allocation(self): def test_create_list_and_delete_allocation(self):
resource_provider = objects.ResourceProvider( resource_provider = objects.ResourceProvider(
context=self.context, context=self.context,
@ -392,24 +433,24 @@ class TestAllocation(ResourceProviderBaseCase):
self.assertEqual(0, len(allocations)) self.assertEqual(0, len(allocations))
def test_destroy(self): def test_destroy(self):
db_rp, db_allocation = self._make_allocation() rp, allocation = self._make_allocation()
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp.uuid) self.context, rp.uuid)
self.assertEqual(1, len(allocations)) self.assertEqual(1, len(allocations))
objects.Allocation._destroy(self.context, db_allocation.id) objects.Allocation._destroy(self.context, allocation.id)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp.uuid) self.context, rp.uuid)
self.assertEqual(0, len(allocations)) self.assertEqual(0, len(allocations))
self.assertRaises(exception.NotFound, objects.Allocation._destroy, self.assertRaises(exception.NotFound, objects.Allocation._destroy,
self.context, db_allocation.id) self.context, allocation.id)
def test_get_allocations_from_db(self): def test_get_allocations_from_db(self):
db_rp, db_allocation = self._make_allocation() rp, allocation = self._make_allocation()
allocations = objects.AllocationList._get_allocations_from_db( allocations = objects.AllocationList._get_allocations_from_db(
self.context, db_rp.uuid) self.context, rp.uuid)
self.assertEqual(1, len(allocations)) self.assertEqual(1, len(allocations))
self.assertEqual(db_rp.id, allocations[0].resource_provider_id) self.assertEqual(rp.id, allocations[0].resource_provider_id)
self.assertEqual(db_allocation.resource_provider_id, self.assertEqual(allocation.resource_provider_id,
allocations[0].resource_provider_id) allocations[0].resource_provider_id)
allocations = objects.AllocationList._get_allocations_from_db( allocations = objects.AllocationList._get_allocations_from_db(
@ -417,35 +458,35 @@ class TestAllocation(ResourceProviderBaseCase):
self.assertEqual(0, len(allocations)) self.assertEqual(0, len(allocations))
def test_get_all_by_resource_provider(self): def test_get_all_by_resource_provider(self):
db_rp, db_allocation = self._make_allocation() rp, allocation = self._make_allocation()
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp.uuid) self.context, rp.uuid)
self.assertEqual(1, len(allocations)) self.assertEqual(1, len(allocations))
self.assertEqual(db_rp.id, allocations[0].resource_provider.id) self.assertEqual(rp.id, allocations[0].resource_provider.id)
self.assertEqual(db_allocation.resource_provider_id, self.assertEqual(allocation.resource_provider_id,
allocations[0].resource_provider.id) allocations[0].resource_provider.id)
def test_get_all_multiple_providers(self): def test_get_all_multiple_providers(self):
# This confirms that the join with resource provider is # This confirms that the join with resource provider is
# behaving. # behaving.
db_rp1, db_allocation1 = self._make_allocation(uuidsentinel.rp1) rp1, allocation1 = self._make_allocation(uuidsentinel.rp1)
db_rp2, db_allocation2 = self._make_allocation(uuidsentinel.rp2) rp2, allocation2 = self._make_allocation(uuidsentinel.rp2)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp1.uuid) self.context, rp1.uuid)
self.assertEqual(1, len(allocations)) self.assertEqual(1, len(allocations))
self.assertEqual(db_rp1.id, allocations[0].resource_provider.id) self.assertEqual(rp1.id, allocations[0].resource_provider.id)
self.assertEqual(db_allocation1.resource_provider_id, self.assertEqual(allocation1.resource_provider_id,
allocations[0].resource_provider.id) allocations[0].resource_provider.id)
# add more allocations for the first resource provider # add more allocations for the first resource provider
# of the same class # of the same class
updates = dict(consumer_id=uuidsentinel.consumer1, updates = dict(consumer_id=uuidsentinel.consumer1,
resource_class_id=RESOURCE_CLASS_ID, resource_class_id=RESOURCE_CLASS_ID,
resource_provider_id=db_rp1.id, resource_provider_id=rp1.id,
used=2) used=2)
objects.Allocation._create_in_db(self.context, updates) objects.Allocation._create_in_db(self.context, updates)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp1.uuid) self.context, rp1.uuid)
self.assertEqual(2, len(allocations)) self.assertEqual(2, len(allocations))
# add more allocations for the first resource provider # add more allocations for the first resource provider
@ -453,18 +494,18 @@ class TestAllocation(ResourceProviderBaseCase):
updates = dict(consumer_id=uuidsentinel.consumer1, updates = dict(consumer_id=uuidsentinel.consumer1,
resource_class_id=fields.ResourceClass.index( resource_class_id=fields.ResourceClass.index(
fields.ResourceClass.IPV4_ADDRESS), fields.ResourceClass.IPV4_ADDRESS),
resource_provider_id=db_rp1.id, resource_provider_id=rp1.id,
used=4) used=4)
objects.Allocation._create_in_db(self.context, updates) objects.Allocation._create_in_db(self.context, updates)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp1.uuid) self.context, rp1.uuid)
self.assertEqual(3, len(allocations)) self.assertEqual(3, len(allocations))
self.assertEqual(db_rp1.uuid, allocations[0].resource_provider.uuid) self.assertEqual(rp1.uuid, allocations[0].resource_provider.uuid)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid( allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.context, db_rp2.uuid) self.context, rp2.uuid)
self.assertEqual(1, len(allocations)) self.assertEqual(1, len(allocations))
self.assertEqual(db_rp2.uuid, allocations[0].resource_provider.uuid) self.assertEqual(rp2.uuid, allocations[0].resource_provider.uuid)
self.assertIn(RESOURCE_CLASS, self.assertIn(RESOURCE_CLASS,
[allocation.resource_class [allocation.resource_class
for allocation in allocations]) for allocation in allocations])

View File

@ -1180,7 +1180,7 @@ object_data = {
'Quotas': '1.2-1fe4cd50593aaf5d36a6dc5ab3f98fb3', 'Quotas': '1.2-1fe4cd50593aaf5d36a6dc5ab3f98fb3',
'QuotasNoOp': '1.2-e041ddeb7dc8188ca71706f78aad41c1', 'QuotasNoOp': '1.2-e041ddeb7dc8188ca71706f78aad41c1',
'RequestSpec': '1.6-c1cb516acdf120d367a42d343ed695b5', 'RequestSpec': '1.6-c1cb516acdf120d367a42d343ed695b5',
'ResourceProvider': '1.0-421c968ee9b2341dd78b0c19c904d4de', 'ResourceProvider': '1.1-7bbcd5ea1c51782692f55489ab08dea6',
'ResourceProviderList': '1.0-82bd48d8d0f7913bbe7266f3835c81bf', 'ResourceProviderList': '1.0-82bd48d8d0f7913bbe7266f3835c81bf',
'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e', 'S3ImageMapping': '1.0-7dd7366a890d82660ed121de9092276e',
'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641', 'SchedulerLimits': '1.0-249c4bd8e62a9b327b7026b7f19cc641',