Do not set allocation.id in AllocationList.create_all()

The _set_allocations method used by AllocationList.create_all is
side-effecty: it sets the 'id' attribute on the list of Allocation
objects that is passed to it.

At the start of the method the incoming Allocation objects are
checked to see if they have already been created, by checking
for an 'id' field.

Meanwhile, _set_allocations is also configured to retry on db
deadlock. The deadlock can happen for a variety of reasons within
the transaction. The original theory, discussed in the original
fix, I2c276dc0125b5b9f7a54a1cd431b1b2f5239e93a, is that it is
during resource provider generation checks. In the associated bug it
looks like it may happen sometimes while inserting allocations.

In either case, if we have gone through the 'for alloc in allocs'
loop at least once, the contents of the 'allocs' list has been
modified to have at least one of the alloc.id fields set. Upon
retry, the 'id' field check at the start of the method will fail,
leading to an ObjectActionError and an eventual 500 at the placement
API level.

This change takes the simplest approach and simply removes the setting
of the 'id' attribute on the allocations in the 'allocs' list. There are
other ways to deal with this, this is the least intrusive. It works
because:

* create_all is only called from the allocation handler in placement,
  and the objects are not used (the response bodies are empty)
* other than the 'id' change, the alloc members in the allocs list
  are otherwise unchanged
* this kind of side-effecty business is dangerous, so let's not
  rely on it

Tests which were relying on the side-effecty business have been adjusted
accordingly.

Conflicts:
      nova/objects/resource_provider.py
      nova/tests/unit/objects/test_resource_provider.py

NOTE(mriedem): The resource_provider.py conflict is due to not
having 31751a7149 in Pike. The
test_resource_provider.py conflict is due to not having
5a1ef8fa86 in Pike. Furthermore,
the functional test had to be adjusted because of not having
db4d6799f1 in Pike.

Change-Id: I3c7aea7d8959a20c3c404bc6616b47336ff40b67
Closes-Bug: #1739453
(cherry picked from commit 3491f3d6f2)
This commit is contained in:
Chris Dent 2017-12-20 18:35:53 +00:00 committed by Matt Riedemann
parent ae7aef15f6
commit cedcef7ecb
3 changed files with 10 additions and 7 deletions

View File

@ -1849,8 +1849,7 @@ class AllocationList(base.ObjectListBase, base.NovaObject):
resource_class_id=rc_id,
consumer_id=alloc.consumer_id,
used=alloc.used)
result = conn.execute(ins_stmt)
alloc.id = result.lastrowid
conn.execute(ins_stmt)
# Generation checking happens here. If the inventory for
# this resource provider changed out from under us,

View File

@ -841,7 +841,6 @@ class TestAllocation(ResourceProviderBaseCase):
disk_allocation.used)
self.assertEqual(DISK_ALLOCATION['consumer_id'],
disk_allocation.consumer_id)
self.assertIsInstance(disk_allocation.id, int)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.ctx, resource_provider.uuid)
@ -997,12 +996,13 @@ class TestAllocation(ResourceProviderBaseCase):
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.ctx, rp.uuid)
self.assertEqual(1, len(allocations))
objects.Allocation._destroy(self.ctx, allocation.id)
allocation_id = allocations[0].id
objects.Allocation._destroy(self.ctx, allocation_id)
allocations = objects.AllocationList.get_all_by_resource_provider_uuid(
self.ctx, rp.uuid)
self.assertEqual(0, len(allocations))
self.assertRaises(exception.NotFound, objects.Allocation._destroy,
self.ctx, allocation.id)
self.ctx, allocation_id)
def test_get_allocations_from_db(self):
rp, allocation = self._make_allocation()

View File

@ -513,9 +513,13 @@ class TestAllocation(test_objects._LocalTest):
consumer_id=uuids.fake_instance,
used=8)
alloc_list = objects.AllocationList(self.context, objects=[obj])
self.assertNotIn("id", obj)
alloc_list.create_all()
self.assertIn("id", obj)
rp_al = resource_provider.AllocationList
saved_allocations = rp_al.get_all_by_resource_provider_uuid(
self.context, rp.uuid)
self.assertEqual(1, len(saved_allocations))
self.assertEqual(obj.used, saved_allocations[0].used)
def test_create_with_id_fails(self):
rp = objects.ResourceProvider(context=self.context,