From 384e6cafd3319dc19e3175e200423e54b7f8801c Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Tue, 7 Aug 2012 12:42:12 -0400 Subject: [PATCH] Remove redundancy in the API Clean up the API a bit based on the discussion we had on the mailing list about removing redundant URLs and queries. This changeset also ensures that each URL matches one function so that the automatically generated documentation will be accurate. Change-Id: I0a9ddd3c096919366492dfdfb78a462f34b0f85d Signed-off-by: Doug Hellmann --- ceilometer/api/v1.py | 210 +++++++++++++++++++++++++------ tests/api/v1/test_list_events.py | 70 +++++------ 2 files changed, 201 insertions(+), 79 deletions(-) diff --git a/ceilometer/api/v1.py b/ceilometer/api/v1.py index 9dcc4f94..f0b57b44 100644 --- a/ceilometer/api/v1.py +++ b/ceilometer/api/v1.py @@ -18,6 +18,55 @@ """Blueprint for version 1 of API. """ +# [ ] / -- information about this version of the API +# +# [ ] /extensions -- list of available extensions +# [ ] /extensions/ -- details about a specific extension +# +# [ ] /sources -- list of known sources (where do we get this?) +# [ ] /sources/components -- list of components which provide metering +# data (where do we get this)? +# +# [x] /projects//resources -- list of resource ids +# [x] /resources -- list of resource ids +# [x] /sources//resources -- list of resource ids +# [x] /users//resources -- list of resource ids +# +# [x] /users -- list of user ids +# [x] /sources//users -- list of user ids +# +# [x] /projects -- list of project ids +# [x] /sources//projects -- list of project ids +# +# [ ] /resources/ -- metadata +# +# [ ] /projects//meters -- list of meters reporting for parent obj +# [ ] /resources//meters -- list of meters reporting for parent obj +# [ ] /sources//meters -- list of meters reporting for parent obj +# [ ] /users//meters -- list of meters reporting for parent obj +# +# [x] /projects//meters/ -- events +# [x] /resources//meters/ -- events +# [x] /sources//meters/ -- events +# [x] /users//meters/ -- events +# +# [ ] /projects//meters//duration -- total time for selected +# meter +# [ ] /resources//meters//duration -- total time for selected +# meter +# [ ] /sources//meters//duration -- total time for selected +# meter +# [ ] /users//meters//duration -- total time for selected meter +# +# [ ] /projects//meters//volume -- total or max volume for +# selected meter +# [ ] /resources//meters//volume -- total or max volume for +# selected meter +# [ ] /sources//meters//volume -- total or max volume for +# selected meter +# [ ] /users//meters//volume -- total or max volume for selected +# meter + import flask from ceilometer.openstack.common import log @@ -33,14 +82,8 @@ blueprint = flask.Blueprint('v1', __name__) ## APIs for working with resources. -@blueprint.route('/resources', defaults={'source': None}) -@blueprint.route('/sources//resources') -@blueprint.route('/users//resources') -@blueprint.route('/projects//resources') -@blueprint.route('/sources//users//resources') -@blueprint.route('/sources//projects//resources') -def list_resources(source=None, user=None, project=None): - """Return a list of resource names. +def _list_resources(source=None, user=None, project=None): + """Return a list of resource identifiers. """ resources = flask.request.storage_conn.get_resources( source=source, @@ -50,58 +93,103 @@ def list_resources(source=None, user=None, project=None): return flask.jsonify(resources=list(resources)) +@blueprint.route('/projects//resources') +def list_resources_by_project(project): + """Return a list of resources owned by the project. + + :param project: The ID of the owning project. + """ + return _list_resources(project=project) + + +@blueprint.route('/resources') +def list_all_resources(): + """Return a list of all known resources. + """ + return _list_resources() + + +@blueprint.route('/sources//resources') +def list_resources_by_source(source): + """Return a list of resources for which a source is reporting + data. + + :param source: The ID of the reporting source. + """ + return _list_resources(source=source) + + +@blueprint.route('/users//resources') +def list_resources_by_user(user): + """Return a list of resources owned by the user. + + :param user: The ID of the owning user. + """ + return _list_resources(user=user) + + ## APIs for working with users. -@blueprint.route('/users', defaults={'source': None}) -@blueprint.route('/sources//users') -def list_users(source): +def _list_users(source=None): """Return a list of user names. """ users = flask.request.storage_conn.get_users(source=source) return flask.jsonify(users=list(users)) +@blueprint.route('/users') +def list_all_users(): + """Return a list of all known user names. + """ + return _list_users() + + +@blueprint.route('/sources//users') +def list_users_by_source(source): + """Return a list of the users for which the source is reporting + data. + + :param source: The ID of the source. + """ + return _list_users(source=source) + + ## APIs for working with projects. -@blueprint.route('/projects', defaults={'source': None}) -@blueprint.route('/sources//projects') -def list_projects(source): +def _list_projects(source=None): """Return a list of project names. """ projects = flask.request.storage_conn.get_projects(source=source) return flask.jsonify(projects=list(projects)) +@blueprint.route('/projects') +def list_all_projects(): + """Return a list of all known project names. + """ + return _list_projects() + + +@blueprint.route('/sources//projects') +def list_projects_by_source(source): + """Return a list project names for which the source is reporting + data. + + :param source: The ID of the source. + """ + return _list_projects(source=source) + + ## APIs for working with events. -@blueprint.route('/projects/') -@blueprint.route('/projects//meters/') -@blueprint.route('/projects//resources/') -@blueprint.route('/projects//resources//meters/') -@blueprint.route('/sources//projects/') -@blueprint.route('/sources//projects//meters/') -@blueprint.route('/sources//projects//resources/') -@blueprint.route( - '/sources//projects//resources//meters/' - ) -@blueprint.route('/users/') -@blueprint.route('/users//meters/') -@blueprint.route('/users//resources/') -@blueprint.route('/users//resources//meters/') -@blueprint.route('/sources//users/') -@blueprint.route('/sources//users//meters/') -@blueprint.route('/sources//users//resources/') -@blueprint.route( - '/sources//users//resources//meters/' - ) -def list_events(user=None, - meter=None, +def _list_events(project=None, resource=None, source=None, - project=None, + user=None, + meter=None, ): """Return a list of raw metering events. """ @@ -113,3 +201,51 @@ def list_events(user=None, ) events = flask.request.storage_conn.get_raw_events(f) return flask.jsonify(events=list(events)) + + +@blueprint.route('/projects//meters/') +def list_events_by_project(project, meter): + """Return a list of raw metering events for the project. + + :param project: The ID of the project. + :param meter: The name of the meter. + """ + return _list_events(project=project, + meter=meter, + ) + + +@blueprint.route('/resources//meters/') +def list_events_by_resource(resource, meter): + """Return a list of raw metering events for the resource. + + :param resource: The ID of the resource. + :param meter: The name of the meter. + """ + return _list_events(resource=resource, + meter=meter, + ) + + +@blueprint.route('/sources//meters/') +def list_events_by_source(source, meter): + """Return a list of raw metering events for the source. + + :param source: The ID of the reporting source. + :param meter: The name of the meter. + """ + return _list_events(source=source, + meter=meter, + ) + + +@blueprint.route('/users//meters/') +def list_events_by_user(user, meter): + """Return a list of raw metering events for the user. + + :param user: The ID of the user. + :param meter: The name of the meter. + """ + return _list_events(user=user, + meter=meter, + ) diff --git a/tests/api/v1/test_list_events.py b/tests/api/v1/test_list_events.py index e3dd6869..01529b3a 100644 --- a/tests/api/v1/test_list_events.py +++ b/tests/api/v1/test_list_events.py @@ -55,7 +55,7 @@ class TestListEvents(tests_api.TestBase): 'instance', 'cumulative', 1, - 'user-id', + 'user-id2', 'project2', 'resource-id-alternate', timestamp=datetime.datetime(2012, 7, 2, 10, 41), @@ -67,52 +67,38 @@ class TestListEvents(tests_api.TestBase): msg2 = meter.meter_message_from_counter(self.counter2) self.conn.record_metering_data(msg2) - def test_empty(self): - data = self.get('/users/no-such-user') + def test_all(self): + data = self.get('/resources') + self.assertEquals(2, len(data['resources'])) + + def test_empty_project(self): + data = self.get('/projects/no-such-project/meters/instance') self.assertEquals({'events': []}, data) - def test_with_user(self): - data = self.get('/users/user-id') - self.assertEquals(2, len(data['events'])) - - def test_with_user_and_meters(self): - data = self.get('/users/user-id/meters/instance') - self.assertEquals(2, len(data['events'])) - - def test_with_user_and_meters_invalid(self): - data = self.get('/users/user-id/meters/no-such-meter') - self.assertEquals(0, len(data['events'])) - - def test_with_source_and_user(self): - data = self.get('/sources/source1/users/user-id') - ids = [r['resource_id'] for r in data['events']] - self.assertEquals(['resource-id'], ids) - - def test_with_resource(self): - data = self.get('/users/user-id/resources/resource-id') - ids = [r['resource_id'] for r in data['events']] - self.assertEquals(['resource-id'], ids) - - - def test_with_project(self): - data = self.get('/projects/project1') - self.assertEquals(1, len(data['events'])) - - def test_with_project_and_meters(self): + def test_by_project(self): data = self.get('/projects/project1/meters/instance') self.assertEquals(1, len(data['events'])) - def test_with_project_and_meters_invalid(self): - data = self.get('/projects/project2/meters/no-such-meter') - self.assertEquals(0, len(data['events'])) + def test_empty_resource(self): + data = self.get('/resources/no-such-resource/meters/instance') + self.assertEquals({'events': []}, data) - def test_with_source_and_project(self): - data = self.get('/sources/source1/projects/project1') - ids = [r['resource_id'] for r in data['events']] - self.assertEquals(['resource-id'], ids) + def test_by_resource(self): + data = self.get('/resources/resource-id/meters/instance') + self.assertEquals(1, len(data['events'])) - def test_with_resource(self): - data = self.get('/projects/project1/resources/resource-id') - ids = [r['resource_id'] for r in data['events']] - self.assertEquals(['resource-id'], ids) + def test_empty_source(self): + data = self.get('/sources/no-such-source/meters/instance') + self.assertEquals({'events': []}, data) + def test_by_source(self): + data = self.get('/sources/source1/meters/instance') + self.assertEquals(1, len(data['events'])) + + def test_empty_user(self): + data = self.get('/users/no-such-user/meters/instance') + self.assertEquals({'events': []}, data) + + def test_by_user(self): + data = self.get('/users/user-id/meters/instance') + self.assertEquals(1, len(data['events']))