Expose resource attributes in the API
This adds the necessary API changes to allow the user to view resource's attributes when making calls to resource show. Implements: blueprint detailed-resource-show Change-Id: Id203478dbd067743d36623e99332ac32c6f96d42
This commit is contained in:
parent
6d8a5cb35c
commit
e9c24bbd85
@ -96,9 +96,12 @@ class ResourceController(object):
|
|||||||
Gets detailed information for a resource
|
Gets detailed information for a resource
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
whitelist = {'with_attr': 'multi'}
|
||||||
|
params = util.get_allowed_params(req.params, whitelist)
|
||||||
res = self.rpc_client.describe_stack_resource(req.context,
|
res = self.rpc_client.describe_stack_resource(req.context,
|
||||||
identity,
|
identity,
|
||||||
resource_name)
|
resource_name,
|
||||||
|
**params)
|
||||||
|
|
||||||
return {'resource': format_resource(req, res)}
|
return {'resource': format_resource(req, res)}
|
||||||
|
|
||||||
|
@ -328,16 +328,20 @@ class EngineClient(object):
|
|||||||
sort_keys=sort_keys,
|
sort_keys=sort_keys,
|
||||||
sort_dir=sort_dir))
|
sort_dir=sort_dir))
|
||||||
|
|
||||||
def describe_stack_resource(self, ctxt, stack_identity, resource_name):
|
def describe_stack_resource(self, ctxt, stack_identity, resource_name,
|
||||||
|
with_attr=None):
|
||||||
"""
|
"""
|
||||||
Get detailed resource information about a particular resource.
|
Get detailed resource information about a particular resource.
|
||||||
:param ctxt: RPC context.
|
:param ctxt: RPC context.
|
||||||
:param stack_identity: Name of the stack.
|
:param stack_identity: Name of the stack.
|
||||||
:param resource_name: the Resource.
|
:param resource_name: the Resource.
|
||||||
"""
|
"""
|
||||||
return self.call(ctxt, self.make_msg('describe_stack_resource',
|
return self.call(ctxt,
|
||||||
|
self.make_msg('describe_stack_resource',
|
||||||
stack_identity=stack_identity,
|
stack_identity=stack_identity,
|
||||||
resource_name=resource_name))
|
resource_name=resource_name,
|
||||||
|
with_attr=with_attr),
|
||||||
|
version='1.2')
|
||||||
|
|
||||||
def find_physical_resource(self, ctxt, physical_resource_id):
|
def find_physical_resource(self, ctxt, physical_resource_id):
|
||||||
"""
|
"""
|
||||||
|
@ -1389,9 +1389,10 @@ class CfnStackControllerTest(common.HeatTestCase):
|
|||||||
args = {
|
args = {
|
||||||
'stack_identity': identity,
|
'stack_identity': identity,
|
||||||
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
||||||
|
'with_attr': None,
|
||||||
}
|
}
|
||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
dummy_req.context, ('describe_stack_resource', args)
|
dummy_req.context, ('describe_stack_resource', args), version='1.2'
|
||||||
).AndReturn(engine_resp)
|
).AndReturn(engine_resp)
|
||||||
|
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
@ -1453,9 +1454,10 @@ class CfnStackControllerTest(common.HeatTestCase):
|
|||||||
args = {
|
args = {
|
||||||
'stack_identity': identity,
|
'stack_identity': identity,
|
||||||
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
'resource_name': dummy_req.params.get('LogicalResourceId'),
|
||||||
|
'with_attr': None,
|
||||||
}
|
}
|
||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
dummy_req.context, ('describe_stack_resource', args)
|
dummy_req.context, ('describe_stack_resource', args), version='1.2'
|
||||||
).AndRaise(heat_exception.ResourceNotFound(
|
).AndRaise(heat_exception.ResourceNotFound(
|
||||||
resource_name='test', stack_name='test'))
|
resource_name='test', stack_name='test'))
|
||||||
|
|
||||||
|
@ -2129,7 +2129,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndReturn(engine_resp)
|
).AndReturn(engine_resp)
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2191,7 +2193,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndReturn(engine_resp)
|
).AndReturn(engine_resp)
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2224,7 +2228,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndRaise(to_remote_error(error))
|
).AndRaise(to_remote_error(error))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2239,6 +2245,47 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
self.assertEqual('StackNotFound', resp.json['error']['type'])
|
self.assertEqual('StackNotFound', resp.json['error']['type'])
|
||||||
self.m.VerifyAll()
|
self.m.VerifyAll()
|
||||||
|
|
||||||
|
def test_show_with_single_attribute(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'show', True)
|
||||||
|
res_name = 'WikiDatabase'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant, 'foo', '1')
|
||||||
|
res_identity = identifier.ResourceIdentifier(resource_name=res_name,
|
||||||
|
**stack_identity)
|
||||||
|
mock_describe = mock.Mock(return_value={'foo': 'bar'})
|
||||||
|
self.controller.rpc_client.describe_stack_resource = mock_describe
|
||||||
|
|
||||||
|
req = self._get(res_identity._tenant_path(), {'with_attr': 'baz'})
|
||||||
|
resp = self.controller.show(req, tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name)
|
||||||
|
|
||||||
|
self.assertEqual({'resource': {'foo': 'bar'}}, resp)
|
||||||
|
args, kwargs = mock_describe.call_args
|
||||||
|
self.assertIn('baz', kwargs['with_attr'])
|
||||||
|
|
||||||
|
def test_show_with_multiple_attributes(self, mock_enforce):
|
||||||
|
self._mock_enforce_setup(mock_enforce, 'show', True)
|
||||||
|
res_name = 'WikiDatabase'
|
||||||
|
stack_identity = identifier.HeatIdentifier(self.tenant, 'foo', '1')
|
||||||
|
res_identity = identifier.ResourceIdentifier(resource_name=res_name,
|
||||||
|
**stack_identity)
|
||||||
|
mock_describe = mock.Mock(return_value={'foo': 'bar'})
|
||||||
|
self.controller.rpc_client.describe_stack_resource = mock_describe
|
||||||
|
|
||||||
|
req = self._get(res_identity._tenant_path())
|
||||||
|
req.environ['QUERY_STRING'] = 'with_attr=a1&with_attr=a2&with_attr=a3'
|
||||||
|
resp = self.controller.show(req, tenant_id=self.tenant,
|
||||||
|
stack_name=stack_identity.stack_name,
|
||||||
|
stack_id=stack_identity.stack_id,
|
||||||
|
resource_name=res_name)
|
||||||
|
|
||||||
|
self.assertEqual({'resource': {'foo': 'bar'}}, resp)
|
||||||
|
args, kwargs = mock_describe.call_args
|
||||||
|
self.assertIn('a1', kwargs['with_attr'])
|
||||||
|
self.assertIn('a2', kwargs['with_attr'])
|
||||||
|
self.assertIn('a3', kwargs['with_attr'])
|
||||||
|
|
||||||
def test_show_nonexist_resource(self, mock_enforce):
|
def test_show_nonexist_resource(self, mock_enforce):
|
||||||
self._mock_enforce_setup(mock_enforce, 'show', True)
|
self._mock_enforce_setup(mock_enforce, 'show', True)
|
||||||
res_name = 'Wibble'
|
res_name = 'Wibble'
|
||||||
@ -2254,7 +2301,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndRaise(to_remote_error(error))
|
).AndRaise(to_remote_error(error))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2284,7 +2333,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndRaise(to_remote_error(error))
|
).AndRaise(to_remote_error(error))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2347,7 +2398,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndReturn(engine_resp)
|
).AndReturn(engine_resp)
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2376,7 +2429,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndRaise(to_remote_error(error))
|
).AndRaise(to_remote_error(error))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
@ -2406,7 +2461,9 @@ class ResourceControllerTest(ControllerTest, common.HeatTestCase):
|
|||||||
rpc_client.EngineClient.call(
|
rpc_client.EngineClient.call(
|
||||||
req.context,
|
req.context,
|
||||||
('describe_stack_resource',
|
('describe_stack_resource',
|
||||||
{'stack_identity': stack_identity, 'resource_name': res_name})
|
{'stack_identity': stack_identity, 'resource_name': res_name,
|
||||||
|
'with_attr': None}),
|
||||||
|
version='1.2'
|
||||||
).AndRaise(to_remote_error(error))
|
).AndRaise(to_remote_error(error))
|
||||||
self.m.ReplayAll()
|
self.m.ReplayAll()
|
||||||
|
|
||||||
|
@ -207,7 +207,8 @@ class EngineRpcAPITestCase(testtools.TestCase):
|
|||||||
def test_describe_stack_resource(self):
|
def test_describe_stack_resource(self):
|
||||||
self._test_engine_api('describe_stack_resource', 'call',
|
self._test_engine_api('describe_stack_resource', 'call',
|
||||||
stack_identity=self.identity,
|
stack_identity=self.identity,
|
||||||
resource_name='LogicalResourceId')
|
resource_name='LogicalResourceId',
|
||||||
|
with_attr=None)
|
||||||
|
|
||||||
def test_find_physical_resource(self):
|
def test_find_physical_resource(self):
|
||||||
self._test_engine_api('find_physical_resource', 'call',
|
self._test_engine_api('find_physical_resource', 'call',
|
||||||
|
Loading…
Reference in New Issue
Block a user