Add recreate test for bug 1798163

Change Icae5038190ab8c7bbdb38d54ae909fcbf9048912 in Rocky
attempts to online migrate missing consumers table records
when listing allocations for a given resource provider. The
problem is when it's doing an insert-from-select, it's not
handling multiple allocations on the same provider for the
same consumer, like you'd have with a compute instance that
has VCPU, MEMORY_MB and DISK_GB allocations against a single
compute node resource provider. As a result, the insert
statement has duplicate consumer IDs in it which results in
a unique constraint violation.

The existing tests never caught this because they tested with
3 unique consumers with a single allocation each.

The functional test added here hits both online data migration
routines: via the API when listing allocations for a resource
provider and the direct online data migration CLI.

Conflicts:
      nova/tests/functional/api/openstack/placement/db/test_consumer.py

NOTE(mriedem): The conflict was due to not having change
I7f5f08691ca3f73073c66c29dddb996fb2c2b266 in Rocky.

Change-Id: Iba56aa6b227b6455d2437e4fabcd296b1b0f06ee
Related-Bug: #1798163
(cherry picked from commit 618b47627d)
This commit is contained in:
Matt Riedemann 2018-10-16 13:47:05 -04:00
parent 237bfcfd82
commit 83396b3254
1 changed files with 35 additions and 5 deletions

View File

@ -11,6 +11,7 @@
# under the License.
from oslo_config import cfg
from oslo_db import exception as db_exc
import sqlalchemy as sa
from nova.api.openstack.placement import db_api
@ -104,7 +105,7 @@ class CreateIncompleteConsumersTestCase(base.TestCase):
self.ctx = self.context
@db_api.placement_context_manager.writer
def _create_incomplete_allocations(self, ctx):
def _create_incomplete_allocations(self, ctx, num_of_consumer_allocs=1):
# Create some allocations with consumers that don't exist in the
# consumers table to represent old allocations that we expect to be
# "cleaned up" with consumers table records that point to the sentinel
@ -113,10 +114,14 @@ class CreateIncompleteConsumersTestCase(base.TestCase):
c2_missing_uuid = uuids.c2_missing
c3_missing_uuid = uuids.c3_missing
for c_uuid in (c1_missing_uuid, c2_missing_uuid, c3_missing_uuid):
ins_stmt = ALLOC_TBL.insert().values(
resource_provider_id=1, resource_class_id=0,
consumer_id=c_uuid, used=1)
ctx.session.execute(ins_stmt)
# Create $num_of_consumer_allocs allocations per consumer with
# different resource classes.
for resource_class_id in range(num_of_consumer_allocs):
ins_stmt = ALLOC_TBL.insert().values(
resource_provider_id=1,
resource_class_id=resource_class_id,
consumer_id=c_uuid, used=1)
ctx.session.execute(ins_stmt)
# Verify there are no records in the projects/users table
project_count = ctx.session.scalar(
sa.select([sa.func.count('*')]).select_from(PROJECT_TBL))
@ -216,6 +221,31 @@ class CreateIncompleteConsumersTestCase(base.TestCase):
res = consumer_obj.create_incomplete_consumers(self.ctx, 10)
self.assertEqual((0, 0), res)
def test_create_incomplete_consumers_multiple_allocs_per_consumer(self):
"""Tests that missing consumer records are created when listing
allocations against a resource provider or running the online data
migration routine when the consumers have multiple allocations on the
same provider.
"""
self._create_incomplete_allocations(self.ctx, num_of_consumer_allocs=2)
# Run the online data migration to migrate one consumer. The batch size
# needs to be large enough to hit more than one consumer for this test
# where each consumer has two allocations.
# FIXME(mriedem): This should not raise a UniqueConstraint error once
# bug 1798163 is fixed.
# res = consumer_obj.create_incomplete_consumers(self.ctx, 2)
# self.assertEqual((2, 2), res)
self.assertRaises(db_exc.DBDuplicateEntry,
consumer_obj.create_incomplete_consumers,
self.ctx, 2)
# Migrate the rest by listing allocations on the resource provider.
rp1 = rp_obj.ResourceProvider(self.ctx, id=1)
# FIXME(mriedem): This should not raise a UniqueConstraint error once
# bug 1798163 is fixed.
self.assertRaises(db_exc.DBDuplicateEntry,
rp_obj.AllocationList.get_all_by_resource_provider,
self.ctx, rp1)
class DeleteConsumerIfNoAllocsTestCase(tb.PlacementDbBaseTestCase):
def test_delete_consumer_if_no_allocs(self):