From fada8b3b938c5f4b8107adfeef3c8b8b0a0c4702 Mon Sep 17 00:00:00 2001 From: Zane Bitter Date: Thu, 22 Nov 2012 11:06:56 +0100 Subject: [PATCH] Improve the identifier class for Resources Enable the resource name to be retrieved from an object. Also allow a ResourceIdentifier to be constructed from only the data passed through the RPC API. This means we can use a single format for identifiers, but still be able to extract important information such as the stack identifier and resource name. Change-Id: Ie9122cb1a835b30eb8e0713a9d5cdcb4a386eda7 Signed-off-by: Zane Bitter --- heat/api/openstack/v1/resources.py | 2 +- heat/engine/identifier.py | 49 ++++++++++++++++++++++------- heat/engine/resources/resource.py | 4 +-- heat/tests/test_api_openstack_v1.py | 20 ++++++------ heat/tests/test_identifier.py | 24 ++++++++++++-- 5 files changed, 71 insertions(+), 28 deletions(-) diff --git a/heat/api/openstack/v1/resources.py b/heat/api/openstack/v1/resources.py index 0cbaab2f..3a16809e 100644 --- a/heat/api/openstack/v1/resources.py +++ b/heat/api/openstack/v1/resources.py @@ -31,7 +31,7 @@ def format_resource(req, stack, keys=[]): return if key == engine_api.RES_ID: - identity = identifier.HeatIdentifier(**value) + identity = identifier.ResourceIdentifier(**value) yield ('links', [util.make_link(req, identity), util.make_link(req, identity.stack(), 'stack')]) elif (key == engine_api.RES_STACK_NAME or diff --git a/heat/engine/identifier.py b/heat/engine/identifier.py index 8cf71c1c..094fbc02 100644 --- a/heat/engine/identifier.py +++ b/heat/engine/identifier.py @@ -72,12 +72,6 @@ class HeatIdentifier(collections.Mapping): urllib.unquote(path.group(2)), urllib.unquote(path.group(3))) - def stack(self): - ''' - Return a HeatIdentifier for the top-level stack - ''' - return HeatIdentifier(self.tenant, self.stack_name, self.stack_id) - def arn(self): ''' Return an ARN of the form: @@ -103,6 +97,10 @@ class HeatIdentifier(collections.Mapping): urllib.quote(self.stack_id, ''), urllib.quote(self.path)) + def _path_components(self): + '''Return a list of the path components''' + return self.path.lstrip('/').split('/') + def __getattr__(self, attr): ''' Return one of the components of the identity when accessed as an @@ -135,10 +133,37 @@ class HeatIdentifier(collections.Mapping): class ResourceIdentifier(HeatIdentifier): - def __init__(self, stack_identifier, resource_id): - path = (stack_identifier.path.rstrip('/') + - '/resources/%s' % resource_id) - super(ResourceIdentifier, self).__init__(stack_identifier.tenant, - stack_identifier.stack_name, - stack_identifier.stack_id, + '''An identifier for a resource''' + + RESOURCE_NAME = 'resource_name' + + def __init__(self, tenant, stack_name, stack_id, path, + resource_name=None): + ''' + Return a new Resource identifier based on the identifier components of + the owning stack and the resource name. + ''' + if resource_name is not None: + path = '/'.join([path.rstrip('/'), 'resources', resource_name]) + super(ResourceIdentifier, self).__init__(tenant, + stack_name, + stack_id, path) + + def __getattr__(self, attr): + ''' + Return one of the components of the identity when accessed as an + attribute. + ''' + + if attr == self.RESOURCE_NAME: + return self._path_components()[-1] + + return HeatIdentifier.__getattr__(self, attr) + + def stack(self): + ''' + Return a HeatIdentifier for the owning stack + ''' + return HeatIdentifier(self.tenant, self.stack_name, self.stack_id, + '/'.join(self._path_components()[:-2])) diff --git a/heat/engine/resources/resource.py b/heat/engine/resources/resource.py index a7ed5315..bd91540e 100644 --- a/heat/engine/resources/resource.py +++ b/heat/engine/resources/resource.py @@ -134,8 +134,8 @@ class Resource(object): def identifier(self): '''Return an identifier for this resource''' - return identifier.ResourceIdentifier(self.stack.identifier(), - self.name) + return identifier.ResourceIdentifier(resource_name=self.name, + **self.stack.identifier()) def parsed_template(self, section=None, default={}): ''' diff --git a/heat/tests/test_api_openstack_v1.py b/heat/tests/test_api_openstack_v1.py index 3ce7a842..f94ca97a 100644 --- a/heat/tests/test_api_openstack_v1.py +++ b/heat/tests/test_api_openstack_v1.py @@ -796,8 +796,8 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '1') - res_identity = identifier.ResourceIdentifier(stack_identity, - res_name) + res_identity = identifier.ResourceIdentifier(resource_name=res_name, + **stack_identity) req = self._get(stack_identity._tenant_path() + '/resources') @@ -873,8 +873,8 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') - res_identity = identifier.ResourceIdentifier(stack_identity, - res_name) + res_identity = identifier.ResourceIdentifier(resource_name=res_name, + **stack_identity) req = self._get(stack_identity._tenant_path()) @@ -930,8 +930,8 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'rubbish', '1') - res_identity = identifier.ResourceIdentifier(stack_identity, - res_name) + res_identity = identifier.ResourceIdentifier(resource_name=res_name, + **stack_identity) req = self._get(res_identity._tenant_path()) @@ -956,8 +956,8 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'wordpress', '6') - res_identity = identifier.ResourceIdentifier(stack_identity, - res_name) + res_identity = identifier.ResourceIdentifier(resource_name=res_name, + **stack_identity) req = self._get(stack_identity._tenant_path()) @@ -998,8 +998,8 @@ class ResourceControllerTest(ControllerTest, unittest.TestCase): res_name = 'WikiDatabase' stack_identity = identifier.HeatIdentifier(self.tenant, 'rubbish', '1') - res_identity = identifier.ResourceIdentifier(stack_identity, - res_name) + res_identity = identifier.ResourceIdentifier(resource_name=res_name, + **stack_identity) req = self._get(res_identity._tenant_path() + '/metadata') diff --git a/heat/tests/test_identifier.py b/heat/tests/test_identifier.py index 92eefb3f..632acbd3 100644 --- a/heat/tests/test_identifier.py +++ b/heat/tests/test_identifier.py @@ -222,6 +222,10 @@ class IdentifierTest(unittest.TestCase): 'stack_name': 's', 'stack_id': 'i'} == hi1) + def test_path_components(self): + hi = identifier.HeatIdentifier('t', 's', 'i', 'p1/p2/p3') + self.assertEqual(hi._path_components(), ['p1', 'p2', 'p3']) + def test_uuid_match(self): uuid = utils.generate_uuid() self.assertTrue(identifier.HeatIdentifier.is_uuid(uuid)) @@ -239,15 +243,29 @@ class IdentifierTest(unittest.TestCase): class ResourceIdentifierTest(unittest.TestCase): def test_resource_init_no_path(self): si = identifier.HeatIdentifier('t', 's', 'i') - ri = identifier.ResourceIdentifier(si, 'r') + ri = identifier.ResourceIdentifier(resource_name='r', **si) self.assertEqual(ri.path, '/resources/r') def test_resource_init_path(self): si = identifier.HeatIdentifier('t', 's', 'i') - pi = identifier.ResourceIdentifier(si, 'p') - ri = identifier.ResourceIdentifier(pi, 'r') + pi = identifier.ResourceIdentifier(resource_name='p', **si) + ri = identifier.ResourceIdentifier(resource_name='r', **pi) self.assertEqual(ri.path, '/resources/p/resources/r') + def test_resource_init_from_dict(self): + hi = identifier.HeatIdentifier('t', 's', 'i', '/resources/r') + ri = identifier.ResourceIdentifier(**hi) + self.assertEqual(ri, hi) + + def test_resource_stack(self): + si = identifier.HeatIdentifier('t', 's', 'i') + ri = identifier.ResourceIdentifier(resource_name='r', **si) + self.assertEqual(ri.stack(), si) + + def test_resource_id(self): + ri = identifier.ResourceIdentifier('t', 's', 'i', '', 'r') + self.assertEqual(ri.resource_name, 'r') + # allows testing of the test directly, shown below if __name__ == '__main__': -- 2.45.2