Merge "placement: allow filter providers in tree"
This commit is contained in:
commit
ede4cf2ef6
|
@ -1411,7 +1411,8 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||||
# 'resources': {
|
# 'resources': {
|
||||||
# 'VCPU': 1,
|
# 'VCPU': 1,
|
||||||
# 'MEMORY_MB': 1024
|
# 'MEMORY_MB': 1024
|
||||||
# }
|
# },
|
||||||
|
# 'in_tree': <uuid>,
|
||||||
# }
|
# }
|
||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
@ -1454,6 +1455,26 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||||
query = query.where(rp.c.name == name)
|
query = query.where(rp.c.name == name)
|
||||||
if uuid:
|
if uuid:
|
||||||
query = query.where(rp.c.uuid == uuid)
|
query = query.where(rp.c.uuid == uuid)
|
||||||
|
if 'in_tree' in filters:
|
||||||
|
# The 'in_tree' parameter is the UUID of a resource provider that
|
||||||
|
# the caller wants to limit the returned providers to only those
|
||||||
|
# within its "provider tree". So, we look up the resource provider
|
||||||
|
# having the UUID specified by the 'in_tree' parameter and grab the
|
||||||
|
# root_provider_id value of that record. We can then ask for only
|
||||||
|
# those resource providers having a root_provider_id of that value.
|
||||||
|
tree_uuid = filters.pop('in_tree')
|
||||||
|
tree_ids = _provider_ids_from_uuid(context, tree_uuid)
|
||||||
|
if tree_ids is None:
|
||||||
|
# List operations should simply return an empty list when a
|
||||||
|
# non-existing resource provider UUID is given.
|
||||||
|
return []
|
||||||
|
root_id = tree_ids.root_id
|
||||||
|
# TODO(jaypipes): Remove this OR condition when root_provider_id
|
||||||
|
# is not nullable in the database and all resource provider records
|
||||||
|
# have populated the root provider ID.
|
||||||
|
where_cond = sa.or_(rp.c.id == root_id,
|
||||||
|
rp.c.root_provider_id == root_id)
|
||||||
|
query = query.where(where_cond)
|
||||||
|
|
||||||
# If 'member_of' has values join with the PlacementAggregates to
|
# If 'member_of' has values join with the PlacementAggregates to
|
||||||
# get those resource providers that are associated with any of the
|
# get those resource providers that are associated with any of the
|
||||||
|
@ -1561,10 +1582,12 @@ class ResourceProviderList(base.ObjectListBase, base.NovaObject):
|
||||||
|
|
||||||
:param context: `nova.context.RequestContext` that may be used to grab
|
:param context: `nova.context.RequestContext` that may be used to grab
|
||||||
a DB connection.
|
a DB connection.
|
||||||
:param filters: Can be `name`, `uuid`, `member_of` or `resources` where
|
:param filters: Can be `name`, `uuid`, `member_of`, `in_tree` or
|
||||||
`member_of` is a list of aggregate uuids and
|
`resources` where `member_of` is a list of aggregate
|
||||||
`resources` is a dict of amounts keyed by resource
|
uuids, `in_tree` is a UUID of a resource provider that
|
||||||
classes.
|
we can use to find the root provider ID of the tree of
|
||||||
|
providers to filter results by and `resources` is a
|
||||||
|
dict of amounts keyed by resource classes.
|
||||||
:type filters: dict
|
:type filters: dict
|
||||||
"""
|
"""
|
||||||
_ensure_rc_cache(context)
|
_ensure_rc_cache(context)
|
||||||
|
|
|
@ -325,6 +325,124 @@ class ResourceProviderTestCase(ResourceProviderBaseCase):
|
||||||
)
|
)
|
||||||
grandchild_rp.set_inventory(inv_list)
|
grandchild_rp.set_inventory(inv_list)
|
||||||
|
|
||||||
|
# Check all providers returned when getting by root UUID
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.root_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(3, len(rps))
|
||||||
|
|
||||||
|
# Check all providers returned when getting by child UUID
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.child_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(3, len(rps))
|
||||||
|
|
||||||
|
# Check all providers returned when getting by grandchild UUID
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(3, len(rps))
|
||||||
|
|
||||||
|
# Make sure that the member_of and uuid filters work with the in_tree
|
||||||
|
# filter
|
||||||
|
|
||||||
|
# No aggregate associations yet, so expect no records when adding a
|
||||||
|
# member_of filter
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'member_of': [uuidsentinel.agg],
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(rps))
|
||||||
|
|
||||||
|
# OK, associate the grandchild with an aggregate and verify that ONLY
|
||||||
|
# the grandchild is returned when asking for the grandchild's tree
|
||||||
|
# along with the aggregate as member_of
|
||||||
|
grandchild_rp.set_aggregates([uuidsentinel.agg])
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'member_of': [uuidsentinel.agg],
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(rps))
|
||||||
|
self.assertEqual(uuidsentinel.grandchild_rp, rps[0].uuid)
|
||||||
|
|
||||||
|
# Try filtering on an unknown UUID and verify no results
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'uuid': uuidsentinel.unknown_rp,
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(rps))
|
||||||
|
|
||||||
|
# And now check that filtering for just the child's UUID along with the
|
||||||
|
# tree produces just a single provider (the child)
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'uuid': uuidsentinel.child_rp,
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(rps))
|
||||||
|
self.assertEqual(uuidsentinel.child_rp, rps[0].uuid)
|
||||||
|
|
||||||
|
# Ensure that the resources filter also continues to work properly with
|
||||||
|
# the in_tree filter. Request resources that none of the providers
|
||||||
|
# currently have and ensure no providers are returned
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
'resources': {
|
||||||
|
'VCPU': 200,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(0, len(rps))
|
||||||
|
|
||||||
|
# And now ask for one VCPU, which should only return us the grandchild
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.grandchild_rp,
|
||||||
|
'resources': {
|
||||||
|
'VCPU': 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(rps))
|
||||||
|
self.assertEqual(uuidsentinel.grandchild_rp, rps[0].uuid)
|
||||||
|
|
||||||
|
# Finally, verify we still get the grandchild if filtering on the
|
||||||
|
# parent's UUID as in_tree
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.child_rp,
|
||||||
|
'resources': {
|
||||||
|
'VCPU': 1,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(rps))
|
||||||
|
self.assertEqual(uuidsentinel.grandchild_rp, rps[0].uuid)
|
||||||
|
|
||||||
allocs = [
|
allocs = [
|
||||||
rp_obj.Allocation(
|
rp_obj.Allocation(
|
||||||
resource_provider=grandchild_rp,
|
resource_provider=grandchild_rp,
|
||||||
|
@ -352,6 +470,50 @@ class ResourceProviderTestCase(ResourceProviderBaseCase):
|
||||||
child_rp.destroy()
|
child_rp.destroy()
|
||||||
root_rp.destroy()
|
root_rp.destroy()
|
||||||
|
|
||||||
|
def test_get_all_in_tree_old_records(self):
|
||||||
|
"""Simulate an old resource provider record in the database that has no
|
||||||
|
root_provider_uuid set and ensure that when selecting all providers in
|
||||||
|
a tree, passing in that old resource provider, that we still get that
|
||||||
|
provider returned.
|
||||||
|
"""
|
||||||
|
# Passing a non-existing resource provider UUID should return an empty
|
||||||
|
# list
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.rp1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual([], rps.objects)
|
||||||
|
|
||||||
|
rp_tbl = rp_obj._RP_TBL
|
||||||
|
conn = self.api_db.get_engine().connect()
|
||||||
|
|
||||||
|
# First, set up a record for an "old-style" resource provider with no
|
||||||
|
# root provider UUID.
|
||||||
|
ins_stmt = rp_tbl.insert().values(
|
||||||
|
id=1,
|
||||||
|
uuid=uuidsentinel.rp1,
|
||||||
|
name='rp-1',
|
||||||
|
root_provider_id=None,
|
||||||
|
parent_provider_id=None,
|
||||||
|
generation=42,
|
||||||
|
)
|
||||||
|
conn.execute(ins_stmt)
|
||||||
|
|
||||||
|
# NOTE(jaypipes): This is just disabling the online data migration that
|
||||||
|
# occurs in _from_db_object() that sets root provider ID to ensure we
|
||||||
|
# don't have any migrations messing with the end result.
|
||||||
|
with mock.patch('nova.objects.resource_provider.'
|
||||||
|
'_set_root_provider_id'):
|
||||||
|
rps = rp_obj.ResourceProviderList.get_all_by_filters(
|
||||||
|
self.ctx,
|
||||||
|
filters={
|
||||||
|
'in_tree': uuidsentinel.rp1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.assertEqual(1, len(rps))
|
||||||
|
|
||||||
def test_destroy_resource_provider(self):
|
def test_destroy_resource_provider(self):
|
||||||
created_resource_provider = rp_obj.ResourceProvider(
|
created_resource_provider = rp_obj.ResourceProvider(
|
||||||
context=self.ctx,
|
context=self.ctx,
|
||||||
|
|
Loading…
Reference in New Issue