diff options
author | Oleksii Chuprykov <ochuprykov@mirantis.com> | 2016-04-11 19:52:30 +0300 |
---|---|---|
committer | Oleksii Chuprykov <ochuprykov@mirantis.com> | 2016-08-10 11:10:44 +0000 |
commit | 816dc3ad3510db2fdba4d6b16b0c3229ad08475f (patch) | |
tree | 3a557ecd5b43ec33db9641400a41bf0b2763a572 | |
parent | b74e638264ecfa1ece5f4c9ef3c55fef01f06def (diff) | |
download | heat-816dc3ad3510db2fdba4d6b16b0c3229ad08475f.tar.gz |
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
(cherry picked from commit 0321d864438fc0c1685de35df429f17fb810571d)
-rw-r--r-- | heat/engine/api.py | 1 | ||||
-rw-r--r-- | heat/engine/service.py | 18 | ||||
-rw-r--r-- | heat/tests/engine/service/test_stack_resources.py | 54 |
3 files changed, 65 insertions, 8 deletions
diff --git a/heat/engine/api.py b/heat/engine/api.py index 46d4f9199..7bb956be9 100644 --- a/heat/engine/api.py +++ b/heat/engine/api.py @@ -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(): diff --git a/heat/engine/service.py b/heat/engine/service.py index 27c8c4bf1..afb781682 100644 --- a/heat/engine/service.py +++ b/heat/engine/service.py @@ -1746,10 +1746,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): diff --git a/heat/tests/engine/service/test_stack_resources.py b/heat/tests/engine/service/test_stack_resources.py index 49276b5bc..8348b24ff 100644 --- a/heat/tests/engine/service/test_stack_resources.py +++ b/heat/tests/engine/service/test_stack_resources.py @@ -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,14 +256,56 @@ 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) stack_id = stk.identifier() |