summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOleksii Chuprykov <ochuprykov@mirantis.com>2016-04-11 19:52:30 +0300
committerOleksii Chuprykov <ochuprykov@mirantis.com>2016-08-10 11:10:44 +0000
commit816dc3ad3510db2fdba4d6b16b0c3229ad08475f (patch)
tree3a557ecd5b43ec33db9641400a41bf0b2763a572
parentb74e638264ecfa1ece5f4c9ef3c55fef01f06def (diff)
downloadheat-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.py1
-rw-r--r--heat/engine/service.py18
-rw-r--r--heat/tests/engine/service/test_stack_resources.py54
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()