Merge "Implement the /v2/samples/<sample-id> API"
This commit is contained in:
commit
3208e3250d
@ -973,6 +973,20 @@ class SamplesController(rest.RestController):
|
||||
return map(Sample.from_db_model,
|
||||
pecan.request.storage_conn.get_samples(f, limit=limit))
|
||||
|
||||
@wsme_pecan.wsexpose(Sample, wtypes.text)
|
||||
def get_one(self, sample_id):
|
||||
"""Return a sample
|
||||
|
||||
:param sample_id: the id of the sample
|
||||
"""
|
||||
f = storage.SampleFilter(message_id=sample_id)
|
||||
|
||||
samples = list(pecan.request.storage_conn.get_samples(f))
|
||||
if len(samples) < 1:
|
||||
raise EntityNotFound(_('Sample'), sample_id)
|
||||
|
||||
return Sample.from_db_model(samples[0])
|
||||
|
||||
|
||||
class Resource(_Base):
|
||||
"""An externally defined object for which samples have been received.
|
||||
|
@ -95,13 +95,15 @@ class SampleFilter(object):
|
||||
:param resource: Optional filter for resource id.
|
||||
:param meter: Optional filter for meter type using the meter name.
|
||||
:param source: Optional source filter.
|
||||
:param message_id: Optional sample_id filter.
|
||||
:param metaquery: Optional filter on the metadata
|
||||
"""
|
||||
def __init__(self, user=None, project=None,
|
||||
start=None, start_timestamp_op=None,
|
||||
end=None, end_timestamp_op=None,
|
||||
resource=None, meter=None,
|
||||
source=None, metaquery={}):
|
||||
source=None, message_id=None,
|
||||
metaquery={}):
|
||||
self.user = user
|
||||
self.project = project
|
||||
self.start = utils.sanitize_timestamp(start)
|
||||
@ -112,6 +114,7 @@ class SampleFilter(object):
|
||||
self.meter = meter
|
||||
self.source = source
|
||||
self.metaquery = metaquery
|
||||
self.message_id = message_id
|
||||
|
||||
|
||||
class EventFilter(object):
|
||||
|
@ -128,6 +128,8 @@ def make_query_from_filter(sample_filter, require_meter=True):
|
||||
q['resource_id'] = sample_filter.resource
|
||||
if sample_filter.source:
|
||||
q['source'] = sample_filter.source
|
||||
if sample_filter.message_id:
|
||||
q['message_id'] = sample_filter.message_id
|
||||
|
||||
# so the samples call metadata resource_metadata, so we convert
|
||||
# to that.
|
||||
|
@ -241,6 +241,7 @@ class Connection(base.Connection):
|
||||
# TODO(shengjie) extra dimensions need to be added as CQ
|
||||
'f:user_id': data['user_id'],
|
||||
'f:project_id': data['project_id'],
|
||||
'f:message_id': data['message_id'],
|
||||
'f:resource_id': data['resource_id'],
|
||||
'f:source': data['source'],
|
||||
# add in reversed_ts here for time range scan
|
||||
@ -707,7 +708,8 @@ def reverse_timestamp(dt):
|
||||
|
||||
def make_query(user=None, project=None, meter=None,
|
||||
resource=None, source=None, start=None, start_op=None,
|
||||
end=None, end_op=None, require_meter=True, query_only=False):
|
||||
end=None, end_op=None, message_id=None, require_meter=True,
|
||||
query_only=False):
|
||||
"""Return a filter query string based on the selected parameters.
|
||||
|
||||
:param user: Optional user-id
|
||||
@ -719,6 +721,7 @@ def make_query(user=None, project=None, meter=None,
|
||||
:param start_op: Optional start timestamp operator, like gt, ge
|
||||
:param end: Optional end timestamp
|
||||
:param end_op: Optional end timestamp operator, like lt, le
|
||||
:param message_id: Optional message_id
|
||||
:param require_meter: If true and the filter does not have a meter,
|
||||
raise an error.
|
||||
:param query_only: If true only returns the filter query,
|
||||
@ -735,6 +738,9 @@ def make_query(user=None, project=None, meter=None,
|
||||
if resource:
|
||||
q.append("SingleColumnValueFilter ('f', 'resource_id', =, 'binary:%s')"
|
||||
% resource)
|
||||
if message_id:
|
||||
q.append("SingleColumnValueFilter ('f', 'message_id', =, 'binary:%s')"
|
||||
% message_id)
|
||||
if source:
|
||||
q.append("SingleColumnValueFilter "
|
||||
"('f', 'source', =, 'binary:%s')" % source)
|
||||
@ -792,6 +798,7 @@ def make_query_from_filter(sample_filter, require_meter=True):
|
||||
sample_filter.start_timestamp_op,
|
||||
sample_filter.end,
|
||||
sample_filter.end_timestamp_op,
|
||||
sample_filter.message_id,
|
||||
require_meter)
|
||||
|
||||
|
||||
|
@ -131,6 +131,8 @@ def make_query_from_filter(sample_filter, require_meter=True):
|
||||
q['resource_id'] = sample_filter.resource
|
||||
if sample_filter.source:
|
||||
q['source'] = sample_filter.source
|
||||
if sample_filter.message_id:
|
||||
q['message_id'] = sample_filter.message_id
|
||||
|
||||
# so the samples call metadata resource_metadata, so we convert
|
||||
# to that.
|
||||
|
@ -159,6 +159,8 @@ def make_query_from_filter(session, query, sample_filter, require_meter=True):
|
||||
query = query.filter_by(project_id=sample_filter.project)
|
||||
if sample_filter.resource:
|
||||
query = query.filter_by(resource_id=sample_filter.resource)
|
||||
if sample_filter.message_id:
|
||||
query = query.filter_by(message_id=sample_filter.message_id)
|
||||
|
||||
if sample_filter.metaquery:
|
||||
query = apply_metaquery_filter(session, query,
|
||||
|
@ -21,6 +21,7 @@
|
||||
|
||||
import base64
|
||||
import datetime
|
||||
import json as jsonutils
|
||||
import logging
|
||||
import testscenarios
|
||||
|
||||
@ -47,6 +48,7 @@ class TestListMeters(FunctionalTest,
|
||||
|
||||
def setUp(self):
|
||||
super(TestListMeters, self).setUp()
|
||||
self.messages = []
|
||||
for cnt in [
|
||||
sample.Sample(
|
||||
'meter.test',
|
||||
@ -131,6 +133,7 @@ class TestListMeters(FunctionalTest,
|
||||
msg = utils.meter_message_from_counter(
|
||||
cnt,
|
||||
self.CONF.publisher.metering_secret)
|
||||
self.messages.append(msg)
|
||||
self.conn.record_metering_data(msg)
|
||||
|
||||
def test_list_meters(self):
|
||||
@ -164,6 +167,33 @@ class TestListMeters(FunctionalTest,
|
||||
self.assertEqual('self.sample4', metadata['tag'])
|
||||
self.assertEqual('prop_value', metadata['properties.prop_1'])
|
||||
|
||||
def test_get_one_sample(self):
|
||||
sample_id = self.messages[1]['message_id']
|
||||
data = self.get_json('/samples/%s' % sample_id)
|
||||
self.assertIn('id', data)
|
||||
self.assertEqual(data, {
|
||||
u'id': sample_id,
|
||||
u'metadata': {u'display_name': u'test-server',
|
||||
u'is_public': u'False',
|
||||
u'size': u'0',
|
||||
u'tag': u'self.sample1',
|
||||
u'util': u'0.47'},
|
||||
u'meter': u'meter.test',
|
||||
u'project_id': u'project-id',
|
||||
u'resource_id': u'resource-id',
|
||||
u'timestamp': u'2012-07-02T11:40:00',
|
||||
u'type': u'cumulative',
|
||||
u'unit': u'',
|
||||
u'user_id': u'user-id',
|
||||
u'volume': 3.0})
|
||||
|
||||
def test_get_not_existing_sample(self):
|
||||
resp = self.get_json('/samples/not_exists', expect_errors=True)
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.assertEqual(jsonutils.loads(resp.body)['error_message']
|
||||
['faultstring'],
|
||||
"Sample not_exists Not Found")
|
||||
|
||||
def test_list_samples_with_dict_metadata(self):
|
||||
data = self.get_json('/samples',
|
||||
q=[{'field':
|
||||
@ -171,7 +201,7 @@ class TestListMeters(FunctionalTest,
|
||||
'op': 'eq',
|
||||
'value': 'sub_prop_value',
|
||||
}])
|
||||
self.assertTrue('id' in data[0])
|
||||
self.assertIn('id', data[0])
|
||||
del data[0]['id'] # Randomly generated
|
||||
self.assertEqual(data, [{
|
||||
u'user_id': u'user-id4',
|
||||
|
Loading…
x
Reference in New Issue
Block a user