diff --git a/ceilometer/api/controllers/v2.py b/ceilometer/api/controllers/v2.py index f4804cdd0..b2a46b331 100644 --- a/ceilometer/api/controllers/v2.py +++ b/ceilometer/api/controllers/v2.py @@ -55,6 +55,10 @@ class _Base(wtypes.Base): def from_db_model(cls, m): return cls(**(m.as_dict())) + @classmethod + def from_db_and_links(cls, m, links): + return cls(links=links, **(m.as_dict())) + def as_dict(self, db_model): valid_keys = inspect.getargspec(db_model.__init__)[0] if 'self' in valid_keys: @@ -66,6 +70,25 @@ class _Base(wtypes.Base): getattr(self, k) != wsme.Unset) +class Link(_Base): + """A link representation + """ + + href = wtypes.text + "The url of a link" + + rel = wtypes.text + "The name of a link" + + @classmethod + def sample(cls): + return cls(href=('http://localhost:8777/v2/meters/volume?' + 'q.field=resource_id&' + 'q.value=bd9431c1-8d69-4ad3-803a-8d4a6b89fd36'), + rel='volume' + ) + + class Query(_Base): """Sample query filter. """ @@ -218,6 +241,15 @@ def _flatten_metadata(metadata): return {} +def _make_link(rel_name, url, type, type_arg, query=None): + query_str = '' + if query: + query_str = '?q.field=%s&q.value=%s' % (query['field'], + query['value']) + return Link(href=('%s/v2/%s/%s%s') % (url, type, type_arg, query_str), + rel=rel_name) + + class Sample(_Base): """A single measurement for a given meter and resource. """ @@ -496,6 +528,9 @@ class Resource(_Base): metadata = {wtypes.text: wtypes.text} "Arbitrary metadata associated with the resource" + links = [Link] + "A list containing a self link and associated meter links" + def __init__(self, metadata={}, **kwds): metadata = _flatten_metadata(metadata) super(Resource, self).__init__(metadata=metadata, **kwds) @@ -508,12 +543,30 @@ class Resource(_Base): timestamp=datetime.datetime.utcnow(), metadata={'name1': 'value1', 'name2': 'value2'}, + links=[Link(href=('http://localhost:8777/v2/resources/' + 'bd9431c1-8d69-4ad3-803a-8d4a6b89fd36'), + rel='self'), + Link(href=('http://localhost:8777/v2/meters/volume?' + 'q.field=resource_id&' + 'q.value=bd9431c1-8d69-4ad3-803a-' + '8d4a6b89fd36'), + rel='volume')], ) class ResourcesController(rest.RestController): """Works on resources.""" + def _resource_links(self, resource_id): + links = [_make_link('self', pecan.request.host_url, 'resources', + resource_id)] + for meter in pecan.request.storage_conn.get_meters(resource= + resource_id): + query = {'field': 'resource_id', 'value': resource_id} + links.append(_make_link(meter.name, pecan.request.host_url, + 'meters', meter.name, query=query)) + return links + @wsme_pecan.wsexpose(Resource, unicode) def get_one(self, resource_id): """Retrieve details about one resource. @@ -522,7 +575,8 @@ class ResourcesController(rest.RestController): """ r = list(pecan.request.storage_conn.get_resources( resource=resource_id))[0] - return Resource.from_db_model(r) + return Resource.from_db_and_links(r, + self._resource_links(resource_id)) @wsme_pecan.wsexpose([Resource], [Query]) def get_all(self, q=[]): @@ -532,7 +586,8 @@ class ResourcesController(rest.RestController): """ kwargs = _query_to_kwargs(q, pecan.request.storage_conn.get_resources) resources = [ - Resource.from_db_model(r) + Resource.from_db_and_links(r, + self._resource_links(r.resource_id)) for r in pecan.request.storage_conn.get_resources(**kwargs)] return resources diff --git a/doc/source/webapi/v2.rst b/doc/source/webapi/v2.rst index 30f7abd65..de31f613c 100644 --- a/doc/source/webapi/v2.rst +++ b/doc/source/webapi/v2.rst @@ -93,3 +93,10 @@ or:: and finally, a JSON-based example:: $ curl -X GET -H 'X-Auth-Token:' -H 'Content-Type:application/json' -d '{"q":[{"field": "timestamp","op": "ge","value":"2013-04-01T13:34:17"}]}' http://localhost:8777/v2/meters + + +Links +===== + +.. autotype:: ceilometer.api.controllers.v2.Link + :members: diff --git a/tests/api/v2/test_list_resources.py b/tests/api/v2/test_list_resources.py index ab9c13596..bc97d8847 100644 --- a/tests/api/v2/test_list_resources.py +++ b/tests/api/v2/test_list_resources.py @@ -308,3 +308,34 @@ class TestListResources(FunctionalTest): [('display_name', 'test-server'), ('tag', 'self.counter'), ]) + + def test_resource_meter_links(self): + counter1 = counter.Counter( + 'instance', + 'cumulative', + '', + 1, + 'user-id', + 'project-id', + 'resource-id', + timestamp=datetime.datetime(2012, 7, 2, 10, 40), + resource_metadata={'display_name': 'test-server', + 'tag': 'self.counter', + } + ) + msg = meter.meter_message_from_counter(counter1, + cfg.CONF.metering_secret, + 'test_list_resources', + ) + self.conn.record_metering_data(msg) + + data = self.get_json('/resources') + links = data[0]['links'] + self.assertEqual(len(links), 2) + self.assertEqual(links[0]['rel'], 'self') + self.assertTrue((self.PATH_PREFIX + '/resources/resource-id') + in links[0]['href']) + self.assertEqual(links[1]['rel'], 'instance') + self.assertTrue((self.PATH_PREFIX + '/meters/instance?' + 'q.field=resource_id&q.value=resource-id') + in links[1]['href'])