Move RC_CACHE in resource_class_cache

In future patches, the RC_CACHE is going to be used
by multiple modules, so we want it in a reasonable
centralized location: resource_class_cache is a fair
choice.

Change-Id: Ic7115d444d7d8b0de91cd098c028b26c39de2a05
This commit is contained in:
Chris Dent 2019-02-28 16:58:04 +00:00
parent f5a1a6d9e6
commit 763fe86fa0
5 changed files with 54 additions and 48 deletions

@ -22,6 +22,7 @@ from placement import handler
from placement import microversion
from placement.objects import resource_provider
from placement import requestlog
from placement import resource_class_cache as rc_cache
from placement import util
@ -98,7 +99,7 @@ def update_database():
ctx = db_api.DbContext()
resource_provider.ensure_trait_sync(ctx)
resource_provider.ensure_resource_classes_sync(ctx)
resource_provider.ensure_rc_cache(ctx)
rc_cache.ensure_rc_cache(ctx)
# NOTE(cdent): Althought project_name is no longer used because of the

@ -58,7 +58,6 @@ _RP_TRAIT_TBL = models.ResourceProviderTrait.__table__
_PROJECT_TBL = models.Project.__table__
_USER_TBL = models.User.__table__
_CONSUMER_TBL = models.Consumer.__table__
_RC_CACHE = None
_RESOURCE_CLASSES_LOCK = 'resource_classes_sync'
_RESOURCE_CLASSES_SYNCED = False
_TRAIT_LOCK = 'trait_sync'
@ -67,20 +66,6 @@ _TRAITS_SYNCED = False
LOG = logging.getLogger(__name__)
@db_api.placement_context_manager.reader
def ensure_rc_cache(ctx):
"""Ensures that a singleton resource class cache has been created in the
module's scope.
:param ctx: `placement.context.RequestContext` that may be used to grab a
DB connection.
"""
global _RC_CACHE
if _RC_CACHE is not None:
return
_RC_CACHE = rc_cache.ResourceClassCache(ctx)
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
# Bug #1760322: If the caller raises an exception, we don't want the trait
# sync rolled back; so use an .independent transaction
@ -243,8 +228,9 @@ def _delete_inventory_from_provider(ctx, rp, to_delete):
).group_by(_ALLOC_TBL.c.resource_class_id)
allocations = ctx.session.execute(allocation_query).fetchall()
if allocations:
resource_classes = ', '.join([_RC_CACHE.string_from_id(alloc[0])
for alloc in allocations])
resource_classes = ', '.join(
[rc_cache.RC_CACHE.string_from_id(alloc[0])
for alloc in allocations])
raise exception.InventoryInUse(resource_classes=resource_classes,
resource_provider=rp.uuid)
@ -266,7 +252,7 @@ def _add_inventory_to_provider(ctx, rp, inv_list, to_add):
adding to resource provider.
"""
for rc_id in to_add:
rc_str = _RC_CACHE.string_from_id(rc_id)
rc_str = rc_cache.RC_CACHE.string_from_id(rc_id)
inv_record = inv_list.find(rc_str)
ins_stmt = _INV_TBL.insert().values(
resource_provider_id=rp.id,
@ -294,7 +280,7 @@ def _update_inventory_for_provider(ctx, rp, inv_list, to_update):
"""
exceeded = []
for rc_id in to_update:
rc_str = _RC_CACHE.string_from_id(rc_id)
rc_str = rc_cache.RC_CACHE.string_from_id(rc_id)
inv_record = inv_list.find(rc_str)
allocation_query = sa.select(
[func.sum(_ALLOC_TBL.c.used).label('usage')]).\
@ -355,7 +341,7 @@ def _add_inventory(context, rp, inventory):
:raises `exception.ResourceClassNotFound` if inventory.resource_class
cannot be found in the DB.
"""
rc_id = _RC_CACHE.id_from_string(inventory.resource_class)
rc_id = rc_cache.RC_CACHE.id_from_string(inventory.resource_class)
inv_list = InventoryList(objects=[inventory])
_add_inventory_to_provider(
context, rp, inv_list, set([rc_id]))
@ -369,7 +355,7 @@ def _update_inventory(context, rp, inventory):
:raises `exception.ResourceClassNotFound` if inventory.resource_class
cannot be found in the DB.
"""
rc_id = _RC_CACHE.id_from_string(inventory.resource_class)
rc_id = rc_cache.RC_CACHE.id_from_string(inventory.resource_class)
inv_list = InventoryList(objects=[inventory])
exceeded = _update_inventory_for_provider(
context, rp, inv_list, set([rc_id]))
@ -384,7 +370,7 @@ def _delete_inventory(context, rp, resource_class):
:raises `exception.ResourceClassNotFound` if resource_class
cannot be found in the DB.
"""
rc_id = _RC_CACHE.id_from_string(resource_class)
rc_id = rc_cache.RC_CACHE.id_from_string(resource_class)
if not _delete_inventory_from_provider(context, rp, [rc_id]):
raise exception.NotFound(
'No inventory of class %s found for delete'
@ -413,7 +399,7 @@ def _set_inventory(context, rp, inv_list):
from a provider that has allocations for that resource class.
"""
existing_resources = _get_current_inventory_resources(context, rp)
these_resources = set([_RC_CACHE.id_from_string(r.resource_class)
these_resources = set([rc_cache.RC_CACHE.id_from_string(r.resource_class)
for r in inv_list.objects])
# Determine which resources we should be adding, deleting and/or
@ -1459,7 +1445,7 @@ class ResourceProviderList(common_obj.ObjectList):
resources = filters.pop('resources', {})
# NOTE(sbauza): We want to key the dict by the resource class IDs
# and we want to make sure those class names aren't incorrect.
resources = {_RC_CACHE.id_from_string(r_name): amount
resources = {rc_cache.RC_CACHE.id_from_string(r_name): amount
for r_name, amount in resources.items()}
rp = sa.alias(_RP_TBL, name="rp")
root_rp = sa.alias(_RP_TBL, name="root_rp")
@ -1695,7 +1681,7 @@ class InventoryList(common_obj.ObjectList):
objs = [
Inventory(
resource_provider=rp,
resource_class=_RC_CACHE.string_from_id(
resource_class=rc_cache.RC_CACHE.string_from_id(
rec['resource_class_id']),
**rec)
for rec in db_inv
@ -1783,7 +1769,7 @@ def _check_capacity_exceeded(ctx, allocs):
#
# We then take the results of the above and determine if any of the
# inventory will have its capacity exceeded.
rc_ids = set([_RC_CACHE.id_from_string(a.resource_class)
rc_ids = set([rc_cache.RC_CACHE.id_from_string(a.resource_class)
for a in allocs])
provider_uuids = set([a.resource_provider.uuid for a in allocs])
provider_ids = set([a.resource_provider.id for a in allocs])
@ -1836,7 +1822,7 @@ def _check_capacity_exceeded(ctx, allocs):
# Ensure that all providers have existing inventory
missing_provs = provider_uuids - provs_with_inv
if missing_provs:
class_str = ', '.join([_RC_CACHE.string_from_id(rc_id)
class_str = ', '.join([rc_cache.RC_CACHE.string_from_id(rc_id)
for rc_id in rc_ids])
provider_str = ', '.join(missing_provs)
raise exception.InvalidInventory(resource_class=class_str,
@ -1846,7 +1832,7 @@ def _check_capacity_exceeded(ctx, allocs):
rp_resource_class_sum = collections.defaultdict(
lambda: collections.defaultdict(int))
for alloc in allocs:
rc_id = _RC_CACHE.id_from_string(alloc.resource_class)
rc_id = rc_cache.RC_CACHE.id_from_string(alloc.resource_class)
rp_uuid = alloc.resource_provider.uuid
if rp_uuid not in res_providers:
res_providers[rp_uuid] = alloc.resource_provider
@ -2125,7 +2111,7 @@ class AllocationList(common_obj.ObjectList):
continue
consumer_id = alloc.consumer.uuid
rp = alloc.resource_provider
rc_id = _RC_CACHE.id_from_string(alloc.resource_class)
rc_id = rc_cache.RC_CACHE.id_from_string(alloc.resource_class)
ins_stmt = _ALLOC_TBL.insert().values(
resource_provider_id=rp.id,
resource_class_id=rc_id,
@ -2177,7 +2163,7 @@ class AllocationList(common_obj.ObjectList):
objs.append(
Allocation(
id=rec['id'], resource_provider=rp,
resource_class=_RC_CACHE.string_from_id(
resource_class=rc_cache.RC_CACHE.string_from_id(
rec['resource_class_id']),
consumer=consumer,
used=rec['used'],
@ -2223,7 +2209,7 @@ class AllocationList(common_obj.ObjectList):
uuid=rec['resource_provider_uuid'],
name=rec['resource_provider_name'],
generation=rec['resource_provider_generation']),
resource_class=_RC_CACHE.string_from_id(
resource_class=rc_cache.RC_CACHE.string_from_id(
rec['resource_class_id']),
consumer=consumer,
used=rec['used'],
@ -2296,7 +2282,8 @@ class Usage(object):
usage=0):
self.resource_class = resource_class
if resource_class_id is not None:
self.resource_class = _RC_CACHE.string_from_id(resource_class_id)
self.resource_class = rc_cache.RC_CACHE.string_from_id(
resource_class_id)
self.usage = int(usage)
@staticmethod
@ -2306,7 +2293,8 @@ class Usage(object):
setattr(target, field, source[field])
if 'resource_class' not in target:
rc_str = _RC_CACHE.string_from_id(source['resource_class_id'])
rc_str = rc_cache.RC_CACHE.string_from_id(
source['resource_class_id'])
target.resource_class = rc_str
target._context = context
@ -2405,7 +2393,7 @@ class ResourceClass(object):
:raises: ResourceClassNotFound if no such resource class was found
"""
rc = _RC_CACHE.all_from_string(name)
rc = rc_cache.RC_CACHE.all_from_string(name)
obj = cls(context, id=rc['id'], name=rc['name'],
updated_at=rc['updated_at'], created_at=rc['created_at'])
return obj
@ -2492,7 +2480,7 @@ class ResourceClass(object):
resource_class=self.name)
self._destroy(self._context, self.id, self.name)
_RC_CACHE.clear()
rc_cache.RC_CACHE.clear()
@staticmethod
@db_api.placement_context_manager.writer
@ -2523,7 +2511,7 @@ class ResourceClass(object):
raise exception.ResourceClassCannotUpdateStandard(
resource_class=self.name)
self._save(self._context, self.id, self.name, updates)
_RC_CACHE.clear()
rc_cache.RC_CACHE.clear()
@staticmethod
@db_api.placement_context_manager.writer
@ -3037,7 +3025,7 @@ def _get_provider_ids_matching(ctx, resources, required_traits,
provs_with_resource = set()
first = True
for rc_id, amount in resources.items():
rc_name = _RC_CACHE.string_from_id(rc_id)
rc_name = rc_cache.RC_CACHE.string_from_id(rc_id)
provs_with_resource = _get_providers_with_resource(
ctx, rc_id, amount, tree_root_id=tree_root_id)
LOG.debug("found %d providers with available %d %s",
@ -3293,7 +3281,7 @@ def _get_trees_matching_all(ctx, resources, required_traits, forbidden_traits,
provs_with_inv = rp_candidates.RPCandidateList()
for rc_id, amount in resources.items():
rc_name = _RC_CACHE.string_from_id(rc_id)
rc_name = rc_cache.RC_CACHE.string_from_id(rc_id)
provs_with_inv_rc = rp_candidates.RPCandidateList()
rc_provs_with_inv = _get_providers_with_resource(
@ -3437,7 +3425,7 @@ def _build_provider_summaries(context, usages, prov_traits):
used = int(usage['used'] or 0)
allocation_ratio = usage['allocation_ratio']
cap = int((usage['total'] - usage['reserved']) * allocation_ratio)
rc_name = _RC_CACHE.string_from_id(rc_id)
rc_name = rc_cache.RC_CACHE.string_from_id(rc_id)
rpsr = ProviderSummaryResource(
resource_class=rc_name,
capacity=cap,
@ -3461,7 +3449,7 @@ def _allocation_request_for_provider(ctx, requested_resources, provider):
resource_requests = [
AllocationRequestResource(
resource_provider=provider,
resource_class=_RC_CACHE.string_from_id(rc_id),
resource_class=rc_cache.RC_CACHE.string_from_id(rc_id),
amount=amount,
) for rc_id, amount in requested_resources.items()
]
@ -3648,7 +3636,7 @@ def _alloc_candidates_multiple_providers(ctx, requested_resources,
tree_dict[rp.root_id][rp.rc_id].append(
AllocationRequestResource(
resource_provider=rp_summary.resource_provider,
resource_class=_RC_CACHE.string_from_id(rp.rc_id),
resource_class=rc_cache.RC_CACHE.string_from_id(rp.rc_id),
amount=requested_resources[rp.rc_id]))
# Next, build up a set of allocation requests. These allocation requests
@ -4069,7 +4057,7 @@ class AllocationCandidates(object):
"""
# Transform resource string names to internal integer IDs
resources = {
_RC_CACHE.id_from_string(key): value
rc_cache.RC_CACHE.id_from_string(key): value
for key, value in request.resources.items()
}
@ -4143,7 +4131,7 @@ class AllocationCandidates(object):
for request in requests.values():
member_of = request.member_of
for rc_name, amount in request.resources.items():
rc_id = _RC_CACHE.id_from_string(rc_name)
rc_id = rc_cache.RC_CACHE.id_from_string(rc_name)
if rc_id not in sharing:
sharing[rc_id] = _get_providers_with_shared_capacity(
context, rc_id, amount, member_of)

@ -17,10 +17,25 @@ from placement.db.sqlalchemy import models
from placement import db_api
from placement import exception
RC_CACHE = None
_RC_TBL = models.ResourceClass.__table__
_LOCKNAME = 'rc_cache'
@db_api.placement_context_manager.reader
def ensure_rc_cache(ctx):
"""Ensures that a singleton resource class cache has been created in the
module's scope.
:param ctx: `placement.context.RequestContext` that may be used to grab a
DB connection.
"""
global RC_CACHE
if RC_CACHE is not None:
return
RC_CACHE = ResourceClassCache(ctx)
@db_api.placement_context_manager.reader
def _refresh_from_db(ctx, cache):
"""Grabs all resource classes from the DB table and populates the

@ -25,6 +25,7 @@ from placement.db.sqlalchemy import migration
from placement import db_api as placement_db
from placement import deploy
from placement.objects import resource_provider
from placement import resource_class_cache as rc_cache
class Database(test_fixtures.GeneratesSchema, test_fixtures.AdHocDbFixture):
@ -75,4 +76,4 @@ class Database(test_fixtures.GeneratesSchema, test_fixtures.AdHocDbFixture):
def cleanup(self):
resource_provider._TRAITS_SYNCED = False
resource_provider._RESOURCE_CLASSES_SYNCED = False
resource_provider._RC_CACHE = None
rc_cache.RC_CACHE = None

@ -25,6 +25,7 @@ from placement import conf
from placement import context
from placement import exception
from placement.objects import resource_provider
from placement import resource_class_cache as rc_cache
_RESOURCE_CLASS_NAME = 'DISK_GB'
@ -113,7 +114,7 @@ _ALLOCATION_BY_CONSUMER_DB = {
def _fake_ensure_cache(ctxt):
cache = resource_provider._RC_CACHE = mock.MagicMock()
cache = rc_cache.RC_CACHE = mock.MagicMock()
cache.string_from_id.return_value = _RESOURCE_CLASS_NAME
cache.id_from_string.return_value = _RESOURCE_CLASS_ID
@ -165,8 +166,8 @@ class TestProviderSummaryNoDB(_TestCase):
class TestInventoryNoDB(_TestCase):
@mock.patch('placement.objects.resource_provider.'
'ensure_rc_cache', side_effect=_fake_ensure_cache)
@mock.patch('placement.resource_class_cache.ensure_rc_cache',
side_effect=_fake_ensure_cache)
@mock.patch('placement.objects.resource_provider.'
'_get_inventory_by_provider_id')
def test_get_all_by_resource_provider(self, mock_get, mock_ensure_cache):