Correct filters for resource search

We can search resources using this filter keys:
type, status, name, action, id, physical_resource_id. For more
information please see heat-stack-resource-search spec in heat-specs
repository and this commit: Iaae88f3b32bc2ba7b41a4078ef3aa8ffc07079b7
We pass `filters` as an argument to sqlalchemy that expects
filters keys to be a columns in a table. There are not such
columns `physical_resource_id`, `type` in Resource table, so
we get error while trying to filter resource by this keys.
Use `nova_instance` instead of `physical_resource_id` and filter
by `type` manually.

Closes-Bug: #1568974

Change-Id: I7b47eae000b244f58a05405746f5fcf9e922aff8
This commit is contained in:
Oleksii Chuprykov 2016-04-11 19:52:30 +03:00
parent fed92fdd6e
commit 0321d86443
3 changed files with 65 additions and 8 deletions

View File

@ -134,6 +134,7 @@ def translate_filters(params):
rpc_api.STACK_OWNER: 'username',
rpc_api.STACK_PARENT: 'owner_id',
rpc_api.STACK_USER_PROJECT_ID: 'stack_user_project_id',
rpc_api.RES_PHYSICAL_ID: 'nova_instance'
}
for key, field in key_map.items():

View File

@ -1757,10 +1757,24 @@ class EngineService(service.Service):
s = self._get_stack(cnxt, stack_identity, show_deleted=True)
stack = parser.Stack.load(cnxt, stack=s)
depth = min(nested_depth, cfg.CONF.max_nested_stack_depth)
res_type = None
if filters is not None:
filters = api.translate_filters(filters)
# There is not corresponding for `type` column in Resource table,
# so sqlalchemy filters can't be used.
res_type = filters.pop('type', None)
def filter_type(res_iter):
for res in res_iter:
if res_type not in res.type():
continue
yield res
if res_type is None:
rsrcs = stack.iter_resources(depth, filters=filters)
else:
rsrcs = filter_type(stack.iter_resources(depth, filters=filters))
return [api.format_stack_resource(resource, detail=with_detail)
for resource in stack.iter_resources(depth,
filters=filters)]
for resource in rsrcs]
@context.request_context
def stack_suspend(self, cnxt, stack_identity):

View File

@ -244,9 +244,9 @@ class StackResourcesServiceTest(common.HeatTestCase):
mock_load.return_value = self.stack
resources = six.itervalues(self.stack)
self.stack.iter_resources = mock.Mock(return_value=resources)
resources = self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
2)
self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
2)
self.stack.iter_resources.assert_called_once_with(2,
filters=None)
@ -256,13 +256,55 @@ class StackResourcesServiceTest(common.HeatTestCase):
mock_load.return_value = self.stack
resources = six.itervalues(self.stack)
self.stack.iter_resources = mock.Mock(return_value=resources)
resources = self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
99)
self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
99)
max_depth = cfg.CONF.max_nested_stack_depth
self.stack.iter_resources.assert_called_once_with(max_depth,
filters=None)
@mock.patch.object(stack.Stack, 'load')
@tools.stack_context('service_resources_list_test_stack')
def test_stack_resources_filter_id(self, mock_load):
mock_load.return_value = self.stack
resources = six.itervalues(self.stack)
self.stack.iter_resources = mock.Mock(return_value=resources)
filters = {'physical_resource_id': '123'}
self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
filters=filters)
expected_filters = {'nova_instance': '123'}
self.stack.iter_resources.assert_called_once_with(
0, filters=expected_filters)
@mock.patch.object(stack.Stack, 'load')
@tools.stack_context('service_resources_list_test_stack')
def test_stack_resources_filter_type(self, mock_load):
mock_load.return_value = self.stack
resources = six.itervalues(self.stack)
self.stack.iter_resources = mock.Mock(return_value=resources)
filters = {'type': 'AWS::EC2::Instance'}
resources = self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
filters=filters)
self.stack.iter_resources.assert_called_once_with(
0, filters={})
self.assertIn('AWS::EC2::Instance', resources[0]['resource_type'])
@mock.patch.object(stack.Stack, 'load')
@tools.stack_context('service_resources_list_test_stack')
def test_stack_resources_filter_type_not_found(self, mock_load):
mock_load.return_value = self.stack
resources = six.itervalues(self.stack)
self.stack.iter_resources = mock.Mock(return_value=resources)
filters = {'type': 'NonExisted'}
resources = self.eng.list_stack_resources(self.ctx,
self.stack.identifier(),
filters=filters)
self.stack.iter_resources.assert_called_once_with(
0, filters={})
self.assertEqual(0, len(resources))
@mock.patch.object(stack.Stack, 'load')
def test_stack_resources_list_deleted_stack(self, mock_load):
stk = tools.setup_stack('resource_list_deleted_stack', self.ctx)