[placement] Filter resource providers by forbidden traits in db
This is the db-side work for filtering resource providers by forbidden traits. Since we control whether forbidden is allowed or not at the API layer, we can add support here in the DB layer such that it works all the time. Subsequent patches will turn on the the functionality in a microversion. Partially implements blueprint placement-forbidden-traits Change-Id: I46796d49931df20b002d1a7e6bb3a6be34dabefa
This commit is contained in:
parent
8bde4042da
commit
e980f6198c
|
@ -1433,7 +1433,11 @@ class ResourceProviderList(base.ObjectListBase, base.VersionedObject):
|
|||
name = filters.pop('name', None)
|
||||
uuid = filters.pop('uuid', None)
|
||||
member_of = filters.pop('member_of', [])
|
||||
required = filters.pop('required', [])
|
||||
required = set(filters.pop('required', []))
|
||||
forbidden = set([trait for trait in required
|
||||
if trait.startswith('!')])
|
||||
required = required - forbidden
|
||||
forbidden = set([trait.lstrip('!') for trait in forbidden])
|
||||
|
||||
resources = filters.pop('resources', {})
|
||||
# NOTE(sbauza): We want to key the dict by the resource class IDs
|
||||
|
@ -1514,6 +1518,17 @@ class ResourceProviderList(base.ObjectListBase, base.VersionedObject):
|
|||
return []
|
||||
query = query.where(rp.c.id.in_(rp_ids))
|
||||
|
||||
# If 'forbidden' has values, filter out those providers that have
|
||||
# that trait as one their traits.
|
||||
if forbidden:
|
||||
trait_map = _trait_ids_from_names(context, forbidden)
|
||||
if len(trait_map) != len(forbidden):
|
||||
missing = forbidden - set(trait_map)
|
||||
raise exception.TraitNotFound(names=', '.join(missing))
|
||||
rp_ids = _get_provider_ids_having_any_trait(context, trait_map)
|
||||
if rp_ids:
|
||||
query = query.where(~rp.c.id.in_(rp_ids))
|
||||
|
||||
if not resources:
|
||||
# Returns quickly the list in case we don't need to check the
|
||||
# resource usage
|
||||
|
|
|
@ -1136,6 +1136,49 @@ class ResourceProviderListTestCase(ResourceProviderBaseCase):
|
|||
[uuidsentinel.agg_1, uuidsentinel.agg_2]})
|
||||
self.assertEqual(0, len(resource_providers))
|
||||
|
||||
def test_get_all_by_required(self):
|
||||
# Create some resource providers and give them each 0 or more traits.
|
||||
# rp_name_0: no traits
|
||||
# rp_name_1: CUSTOM_TRAIT_A
|
||||
# rp_name_2: CUSTOM_TRAIT_A, CUSTOM_TRAIT_B
|
||||
# rp_name_3: CUSTOM_TRAIT_A, CUSTOM_TRAIT_B, CUSTOM_TRAIT_C
|
||||
trait_names = ['CUSTOM_TRAIT_A', 'CUSTOM_TRAIT_B',
|
||||
'CUSTOM_TRAIT_C']
|
||||
trait_objects = []
|
||||
for trait in trait_names:
|
||||
trait_object = rp_obj.Trait(self.ctx, name=trait)
|
||||
trait_object.create()
|
||||
trait_objects.append(trait_object)
|
||||
for rp_i in [0, 1, 2, 3]:
|
||||
uuid = getattr(uuidsentinel, 'rp_uuid_' + str(rp_i))
|
||||
name = 'rp_name_' + str(rp_i)
|
||||
rp = rp_obj.ResourceProvider(self.ctx, name=name, uuid=uuid)
|
||||
rp.create()
|
||||
if rp_i:
|
||||
traits = trait_objects[0:rp_i]
|
||||
rp.set_traits(traits)
|
||||
|
||||
# Three rps (1, 2, 3) should have CUSTOM_TRAIT_A
|
||||
custom_a_rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||
self.ctx, filters={'required': ['CUSTOM_TRAIT_A']})
|
||||
self.assertEqual(3, len(custom_a_rps))
|
||||
rp_names = [a_rp.name for a_rp in custom_a_rps]
|
||||
expected_names = ['rp_name_%s' % i for i in [1, 2, 3]]
|
||||
self.assertEqual(expected_names, sorted(rp_names))
|
||||
|
||||
# One rp (rp 1) if we forbid CUSTOM_TRAIT_B, with a single trait of
|
||||
# CUSTOM_TRAIT_A
|
||||
custom_a_rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||
self.ctx,
|
||||
filters={'required': ['CUSTOM_TRAIT_A', '!CUSTOM_TRAIT_B']})
|
||||
self.assertEqual(1, len(custom_a_rps))
|
||||
self.assertEqual(uuidsentinel.rp_uuid_1, custom_a_rps[0].uuid)
|
||||
self.assertEqual('rp_name_1', custom_a_rps[0].name)
|
||||
traits = rp_obj.TraitList.get_all_by_resource_provider(
|
||||
self.ctx, custom_a_rps[0])
|
||||
self.assertEqual(1, len(traits))
|
||||
self.assertEqual('CUSTOM_TRAIT_A', traits[0].name)
|
||||
|
||||
|
||||
class TestResourceProviderAggregates(test.NoDBTestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue