Merge "Ensure that os-traits sync is attempted only at start of process"

This commit is contained in:
Zuul 2018-06-20 18:23:01 +00:00 committed by Gerrit Code Review
commit 4938584f6b
10 changed files with 35 additions and 36 deletions

View File

@ -38,3 +38,8 @@ def configure(conf):
def get_placement_engine(): def get_placement_engine():
return placement_context_manager.get_legacy_facade().get_engine() return placement_context_manager.get_legacy_facade().get_engine()
@enginefacade.transaction_context_provider
class DbContext(object):
"""Stub class for db session handling outside of web requests."""

View File

@ -16,9 +16,11 @@ import oslo_middleware
from oslo_middleware import cors from oslo_middleware import cors
from nova.api.openstack.placement import auth from nova.api.openstack.placement import auth
from nova.api.openstack.placement import db_api
from nova.api.openstack.placement import fault_wrap from nova.api.openstack.placement import fault_wrap
from nova.api.openstack.placement import handler from nova.api.openstack.placement import handler
from nova.api.openstack.placement import microversion from nova.api.openstack.placement import microversion
from nova.api.openstack.placement.objects import resource_provider
from nova.api.openstack.placement import requestlog from nova.api.openstack.placement import requestlog
from nova.api.openstack.placement import util from nova.api.openstack.placement import util
@ -82,6 +84,14 @@ def deploy(conf):
return application return application
def update_database():
"""Do any database updates required at process boot time, such as
updating the traits database.
"""
ctx = db_api.DbContext()
resource_provider.ensure_trait_sync(ctx)
# NOTE(cdent): Althought project_name is no longer used because of the # NOTE(cdent): Althought project_name is no longer used because of the
# resolution of https://bugs.launchpad.net/nova/+bug/1734491, loadapp() # resolution of https://bugs.launchpad.net/nova/+bug/1734491, loadapp()
# is considered a public interface for the creation of a placement # is considered a public interface for the creation of a placement
@ -98,4 +108,5 @@ def loadapp(config, project_name=NAME):
backwards compatibility backwards compatibility
""" """
application = deploy(config) application = deploy(config)
update_database()
return application return application

View File

@ -123,7 +123,7 @@ def _trait_sync(ctx):
pass # some other process sync'd, just ignore pass # some other process sync'd, just ignore
def _ensure_trait_sync(ctx): def ensure_trait_sync(ctx):
"""Ensures that the os_traits library is synchronized to the traits db. """Ensures that the os_traits library is synchronized to the traits db.
If _TRAITS_SYNCED is False then this process has not tried to update the If _TRAITS_SYNCED is False then this process has not tried to update the
@ -1448,7 +1448,6 @@ class ResourceProviderList(base.ObjectListBase, base.VersionedObject):
:type filters: dict :type filters: dict
""" """
_ensure_rc_cache(context) _ensure_rc_cache(context)
_ensure_trait_sync(context)
resource_providers = cls._get_all_by_filters_from_db(context, filters) resource_providers = cls._get_all_by_filters_from_db(context, filters)
return base.obj_make_list(context, cls(context), return base.obj_make_list(context, cls(context),
ResourceProvider, resource_providers) ResourceProvider, resource_providers)
@ -2387,7 +2386,6 @@ class Trait(base.VersionedObject, base.TimestampedObject):
@staticmethod @staticmethod
@db_api.placement_context_manager.writer # trait sync can cause a write @db_api.placement_context_manager.writer # trait sync can cause a write
def _get_by_name_from_db(context, name): def _get_by_name_from_db(context, name):
_ensure_trait_sync(context)
result = context.session.query(models.Trait).filter_by( result = context.session.query(models.Trait).filter_by(
name=name).first() name=name).first()
if not result: if not result:
@ -2437,7 +2435,6 @@ class TraitList(base.ObjectListBase, base.VersionedObject):
@staticmethod @staticmethod
@db_api.placement_context_manager.writer # trait sync can cause a write @db_api.placement_context_manager.writer # trait sync can cause a write
def _get_all_from_db(context, filters): def _get_all_from_db(context, filters):
_ensure_trait_sync(context)
if not filters: if not filters:
filters = {} filters = {}
@ -3826,7 +3823,6 @@ class AllocationCandidates(base.VersionedObject):
according to `limit`. according to `limit`.
""" """
_ensure_rc_cache(context) _ensure_rc_cache(context)
_ensure_trait_sync(context)
alloc_reqs, provider_summaries = cls._get_by_requests( alloc_reqs, provider_summaries = cls._get_by_requests(
context, requests, limit=limit, group_policy=group_policy) context, requests, limit=limit, group_policy=group_policy)
return cls( return cls(

View File

@ -312,7 +312,10 @@ class TestCase(testtools.TestCase):
utils._IS_NEUTRON = None utils._IS_NEUTRON = None
# Reset the traits sync and rc cache flags # Reset the traits sync and rc cache flags
resource_provider._TRAITS_SYNCED = False def _reset_traits():
resource_provider._TRAITS_SYNCED = False
_reset_traits()
self.addCleanup(_reset_traits)
resource_provider._RC_CACHE = None resource_provider._RC_CACHE = None
# Reset the global QEMU version flag. # Reset the global QEMU version flag.
images.QEMU_VERSION = None images.QEMU_VERSION = None

View File

@ -14,6 +14,7 @@
from oslo_utils import uuidutils from oslo_utils import uuidutils
from nova.api.openstack.placement import deploy
from nova.api.openstack.placement import exception from nova.api.openstack.placement import exception
from nova.api.openstack.placement.objects import consumer as consumer_obj from nova.api.openstack.placement.objects import consumer as consumer_obj
from nova.api.openstack.placement.objects import project as project_obj from nova.api.openstack.placement.objects import project as project_obj
@ -55,26 +56,18 @@ class PlacementDbBaseTestCase(test.NoDBTestCase):
self.useFixture(fixtures.Database()) self.useFixture(fixtures.Database())
self.placement_db = self.useFixture( self.placement_db = self.useFixture(
fixtures.Database(database='placement')) fixtures.Database(database='placement'))
# Reset the _TRAITS_SYNCED global before we start and after
# we are done since other tests (notably the gabbi tests)
# may have caused it to change.
self._reset_traits_synced()
self.addCleanup(self._reset_traits_synced)
self.ctx = context.RequestContext('fake-user', 'fake-project') self.ctx = context.RequestContext('fake-user', 'fake-project')
self.user_obj = user_obj.User(self.ctx, external_id='fake-user') self.user_obj = user_obj.User(self.ctx, external_id='fake-user')
self.user_obj.create() self.user_obj.create()
self.project_obj = project_obj.Project( self.project_obj = project_obj.Project(
self.ctx, external_id='fake-project') self.ctx, external_id='fake-project')
self.project_obj.create() self.project_obj.create()
# Do database syncs, such as traits sync.
deploy.update_database()
# For debugging purposes, populated by _create_provider and used by # For debugging purposes, populated by _create_provider and used by
# _validate_allocation_requests to make failure results more readable. # _validate_allocation_requests to make failure results more readable.
self.rp_uuid_to_name = {} self.rp_uuid_to_name = {}
@staticmethod
def _reset_traits_synced():
"""Reset the _TRAITS_SYNCED boolean to base state."""
rp_obj._TRAITS_SYNCED = False
def _create_provider(self, name, *aggs, **kwargs): def _create_provider(self, name, *aggs, **kwargs):
parent = kwargs.get('parent') parent = kwargs.get('parent')
root = kwargs.get('root') root = kwargs.get('root')

View File

@ -2032,6 +2032,10 @@ class ResourceProviderTraitTestCase(tb.PlacementDbBaseTestCase):
# in an exception, the sync transaction rolled back and the table # in an exception, the sync transaction rolled back and the table
# stayed empty; but _TRAITS_SYNCED got set to True, so it didn't resync # stayed empty; but _TRAITS_SYNCED got set to True, so it didn't resync
# next time. # next time.
# NOTE(cdent): With change Ic87518948ed5bf4ab79f9819cd94714e350ce265
# syncing is no longer done in the same way, so the bug fix that this
# test was testing is gone, but this test has been left in place to
# make sure we still get behavior we expect.
try: try:
rp_obj.Trait.get_by_name(self.ctx, 'CUSTOM_GOLD') rp_obj.Trait.get_by_name(self.ctx, 'CUSTOM_GOLD')
except exception.TraitNotFound: except exception.TraitNotFound:
@ -2193,25 +2197,6 @@ class ResourceProviderTraitTestCase(tb.PlacementDbBaseTestCase):
rp_obj.TraitList.get_all(self.ctx, rp_obj.TraitList.get_all(self.ctx,
filters={'associated': False})) filters={'associated': False}))
def test_sync_standard_traits(self):
"""Tests that on a clean DB, we have zero traits in the DB but after
list all traits, os_traits have been synchronized.
"""
std_traits = os_traits.get_traits()
conn = self.placement_db.get_engine().connect()
def _db_traits(conn):
sel = sa.select([rp_obj._TRAIT_TBL.c.name])
return [r[0] for r in conn.execute(sel).fetchall()]
self.assertEqual([], _db_traits(conn))
all_traits = [trait.name for trait in
rp_obj.TraitList.get_all(self.ctx)]
self.assertEqual(set(std_traits), set(all_traits))
# confirm with a raw request
self.assertEqual(set(std_traits), set(_db_traits(conn)))
class SharedProviderTestCase(tb.PlacementDbBaseTestCase): class SharedProviderTestCase(tb.PlacementDbBaseTestCase):
"""Tests that the queries used to determine placement in deployments with """Tests that the queries used to determine placement in deployments with

View File

@ -88,6 +88,9 @@ class APIFixture(fixture.GabbiFixture):
self.placement_db_fixture.reset() self.placement_db_fixture.reset()
self.api_db_fixture.reset() self.api_db_fixture.reset()
self.main_db_fixture.reset() self.main_db_fixture.reset()
# Do this now instead of waiting for the WSGI app to start so that
# fixtures can have traits.
deploy.update_database()
os.environ['RP_UUID'] = uuidutils.generate_uuid() os.environ['RP_UUID'] = uuidutils.generate_uuid()
os.environ['RP_NAME'] = uuidutils.generate_uuid() os.environ['RP_NAME'] = uuidutils.generate_uuid()

View File

@ -85,6 +85,7 @@ class _IntegratedTestBase(test.TestCase):
nova.tests.unit.image.fake.stub_out_image_service(self) nova.tests.unit.image.fake.stub_out_image_service(self)
self.useFixture(cast_as_call.CastAsCall(self)) self.useFixture(cast_as_call.CastAsCall(self))
self.useFixture(nova_fixtures.Database(database='placement'))
placement = self.useFixture(nova_fixtures.PlacementFixture()) placement = self.useFixture(nova_fixtures.PlacementFixture())
self.placement_api = placement.api self.placement_api = placement.api

View File

@ -338,7 +338,7 @@ class TestTraits(_TestCase):
synced = resource_provider._TRAITS_SYNCED synced = resource_provider._TRAITS_SYNCED
self.assertFalse(synced) self.assertFalse(synced)
# Sync the traits # Sync the traits
resource_provider._ensure_trait_sync(self.context) resource_provider.ensure_trait_sync(self.context)
synced = resource_provider._TRAITS_SYNCED synced = resource_provider._TRAITS_SYNCED
self.assertTrue(synced) self.assertTrue(synced)

View File

@ -478,6 +478,8 @@ class TestPlacementFixture(testtools.TestCase):
self.useFixture(conf_fixture.ConfFixture()) self.useFixture(conf_fixture.ConfFixture())
# We need PlacementPolicyFixture because placement-api checks policy. # We need PlacementPolicyFixture because placement-api checks policy.
self.useFixture(policy_fixture.PlacementPolicyFixture()) self.useFixture(policy_fixture.PlacementPolicyFixture())
# Database is needed to start placement API
self.useFixture(fixtures.Database(database='placement'))
def test_responds_to_version(self): def test_responds_to_version(self):
"""Ensure the Placement server responds to calls sensibly.""" """Ensure the Placement server responds to calls sensibly."""