From ea76c9be8c6a84cd78c324bc67a3665bf6d464b2 Mon Sep 17 00:00:00 2001 From: Fei Long Wang Date: Sun, 28 Jul 2013 16:45:29 +0800 Subject: [PATCH] Fixes Hbase metadata query return wrong result Based on current implement of Hbase metadata query in Ceilometer, it's trying to compare the metadata value with the metadata stored in table "resource" instead of table "meter", when user query a specific meter name. As a result, the query result will be wrong. See: https://github.com/openstack/ceilometer/blob/master/ceilometer/storage/impl_hbase.py#L441 https://github.com/openstack/ceilometer/blob/master/ceilometer/storage/impl_mongodb.py#L535 Fixes bug 1205759 Change-Id: I3f8168fa5371985ac135b0a9d41e76d7bc71c4a7 --- ceilometer/storage/impl_hbase.py | 10 +-- tests/api/v2/test_list_meters_scenarios.py | 94 +++++++++++++++++++++- 2 files changed, 98 insertions(+), 6 deletions(-) diff --git a/ceilometer/storage/impl_hbase.py b/ceilometer/storage/impl_hbase.py index a0636e768..84c1fd8d3 100644 --- a/ceilometer/storage/impl_hbase.py +++ b/ceilometer/storage/impl_hbase.py @@ -418,7 +418,6 @@ class Connection(base.Connection): data['timestamp'] = timeutils.parse_strtime(data['timestamp']) return models.Sample(**data) - resource_table = self.conn.table(self.RESOURCE_TABLE) meter_table = self.conn.table(self.METER_TABLE) q, start, stop = make_query_from_filter(sample_filter, @@ -437,11 +436,10 @@ class Connection(base.Connection): if limit == 0: break if len(metaquery) > 0: - # metaquery checks resource table - resource = resource_table.row(meter['f:resource_id']) - for k, v in metaquery.iteritems(): - if resource['f:r_' + k.split('.', 1)[1]] != v: + message = json.loads(meter['f:message']) + metadata = message['resource_metadata'] + if metadata[k.split('.', 1)[1]] != v: break # if one metaquery doesn't match, break else: if limit: @@ -764,6 +762,8 @@ def make_query(user=None, project=None, meter=None, # query other tables should have no start and end passed in if meter: start_row, end_row = _make_rowkey_scan(meter, rts_start, rts_end) + q.append("SingleColumnValueFilter " + "('f', 'counter_name', =, 'binary:%s')" % meter) elif require_meter: raise RuntimeError('Missing required meter specifier') else: diff --git a/tests/api/v2/test_list_meters_scenarios.py b/tests/api/v2/test_list_meters_scenarios.py index 9febc3367..2f77e52e6 100644 --- a/tests/api/v2/test_list_meters_scenarios.py +++ b/tests/api/v2/test_list_meters_scenarios.py @@ -71,7 +71,7 @@ class TestListMeters(FunctionalTest, 'resource-id', timestamp=datetime.datetime(2012, 7, 2, 11, 40), resource_metadata={'display_name': 'test-server', - 'tag': 'self.counter'}), + 'tag': 'self.counter1'}), sample.Sample( 'meter.mine', 'gauge', @@ -123,6 +123,34 @@ class TestListMeters(FunctionalTest, set(['meter.test', 'meter.mine'])) + def test_list_meters_metadata_query(self): + data = self.get_json('/meters/meter.test', + q=[{'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter1', + }],) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['resource_id'] for r in data), + set(['resource-id'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.test'])) + + def test_list_meters_multi_metadata_query(self): + data = self.get_json('/meters/meter.test', + q=[{'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter1', + }, + {'field': 'metadata.display_name', + 'op': 'eq', + 'value': 'test-server', + }],) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['resource_id'] for r in data), + set(['resource-id'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.test'])) + def test_with_resource(self): data = self.get_json('/meters', q=[{'field': 'resource_id', 'value': 'resource-id', @@ -130,6 +158,22 @@ class TestListMeters(FunctionalTest, ids = set(r['name'] for r in data) self.assertEquals(set(['meter.test']), ids) + def test_with_resource_and_metadata_query(self): + data = self.get_json('/meters/meter.mine', + q=[{'field': 'resource_id', + 'op': 'eq', + 'value': 'resource-id2', + }, + {'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter2', + }]) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['resource_id'] for r in data), + set(['resource-id2'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.mine'])) + def test_with_source(self): data = self.get_json('/meters', q=[{'field': 'source', 'value': 'test_source', @@ -140,6 +184,22 @@ class TestListMeters(FunctionalTest, 'resource-id3', 'resource-id4']), ids) + def test_with_source_and_metadata_query(self): + data = self.get_json('/meters/meter.mine', + q=[{'field': 'source', + 'op': 'eq', + 'value': 'test_source', + }, + {'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter2', + }]) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['source'] for r in data), + set(['test_source'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.mine'])) + def test_with_source_non_existent(self): data = self.get_json('/meters', q=[{'field': 'source', @@ -164,6 +224,22 @@ class TestListMeters(FunctionalTest, rids = set(r['resource_id'] for r in data) self.assertEquals(set(['resource-id', 'resource-id2']), rids) + def test_with_user_and_metadata_query(self): + data = self.get_json('/meters/meter.test', + q=[{'field': 'user_id', + 'op': 'eq', + 'value': 'user-id', + }, + {'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter1', + }]) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['user_id'] for r in data), + set(['user-id'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.test'])) + def test_with_user_non_existent(self): data = self.get_json('/meters', q=[{'field': 'user_id', @@ -181,6 +257,22 @@ class TestListMeters(FunctionalTest, ids = set(r['resource_id'] for r in data) self.assertEquals(set(['resource-id3', 'resource-id4']), ids) + def test_with_project_and_metadata_query(self): + data = self.get_json('/meters/meter.test', + q=[{'field': 'project_id', + 'op': 'eq', + 'value': 'project-id', + }, + {'field': 'metadata.tag', + 'op': 'eq', + 'value': 'self.counter1', + }]) + self.assertEquals(1, len(data)) + self.assertEquals(set(r['project_id'] for r in data), + set(['project-id'])) + self.assertEquals(set(r['counter_name'] for r in data), + set(['meter.test'])) + def test_with_project_non_existent(self): data = self.get_json('/meters', q=[{'field': 'project_id',