Browse Source

Meter Get_Meter_Byname implemented

Ceilometer API, Meter get_meter_byname implemented and tested.

Change-Id: I4f038213797c8be7eb8e66822941afda3921a7dd
Xiao Tan 3 years ago
parent
commit
f3dada5dd2

+ 4
- 0
kiloeyes/api/ceilometer_api_v2.py View File

@@ -32,3 +32,7 @@ class V2API(object):
32 32
     @resource_api.Restify('/v2.0/meters', method='post')
33 33
     def post_meters(self, req, res):
34 34
         res.status = '501 Not Implemented'
35
+
36
+    @resource_api.Restify('/v2.0/meters/{meter_name}', method='get')
37
+    def get_meter_byname(self, req, res, meter_name):
38
+        res.status = '501 Not Implemented'

+ 50
- 17
kiloeyes/tests/v2/elasticsearch/test_meters.py View File

@@ -58,6 +58,22 @@ class TestMeterDispatcher(base.BaseTestCase):
58 58
             with mock.patch.object(requests, 'put', return_value=put_res):
59 59
                 self.dispatcher = meters.MeterDispatcher({})
60 60
 
61
+        self.response_str = """
62
+        {"aggregations":{"by_name":{"doc_count_error_upper_bound":0,
63
+        "sum_other_doc_count":0,"buckets":[{"key":"BABMGD","doc_count":300,
64
+        "by_dim":{"buckets":[{"key": "64e6ce08b3b8547b7c32e5cfa5b7d81f",
65
+        "doc_count":300,"meters":{"hits":{"hits":[{ "_type": "metrics",
66
+        "_id": "AVOziWmP6-pxt0dRmr7j", "_index": "data_20160401000000",
67
+        "_source":{"name":"BABMGD", "value": 4,
68
+        "timestamp": 1461337094000,
69
+        "dimensions_hash": "0afdb86f508962bb5d8af52df07ef35a",
70
+        "project_id": "35b17138-b364-4e6a-a131-8f3099c5be68",
71
+        "tenant_id": "bd9431c1-8d69-4ad3-803a-8d4a6b89fd36",
72
+        "user_agent": "openstack", "dimensions": null,
73
+        "user": "admin", "value_meta": null, "tenant": "admin",
74
+        "user_id": "efd87807-12d2-4b38-9c70-5f5c2ac427ff"}}]}}}]}}]}}}
75
+        """
76
+
61 77
     def test_initialization(self):
62 78
         # test that the kafka connection uri should be 'fake' as it was passed
63 79
         # in from configuration
@@ -104,27 +120,12 @@ class TestMeterDispatcher(base.BaseTestCase):
104 120
         req.get_param.side_effect = _side_effect
105 121
 
106 122
         req_result = mock.Mock()
107
-        response_str = """
108
-        {"aggregations":{"by_name":{"doc_count_error_upper_bound":0,
109
-        "sum_other_doc_count":0,"buckets":[{"key":"BABMGD","doc_count":300,
110
-        "by_dim":{"buckets":[{"key": "64e6ce08b3b8547b7c32e5cfa5b7d81f",
111
-        "doc_count":300,"meters":{"hits":{"hits":[{ "_type": "metrics",
112
-        "_id": "AVOziWmP6-pxt0dRmr7j", "_index": "data_20160401000000",
113
-        "_source":{"name":"BABMGD", "value": 4,
114
-        "timestamp": 1461337094000,
115
-        "dimensions_hash": "0afdb86f508962bb5d8af52df07ef35a",
116
-        "project_id": "35b17138-b364-4e6a-a131-8f3099c5be68",
117
-        "tenant_id": "bd9431c1-8d69-4ad3-803a-8d4a6b89fd36",
118
-        "user_agent": "openstack", "dimensions": null,
119
-        "user": "admin", "value_meta": null, "tenant": "admin",
120
-        "user_id": "efd87807-12d2-4b38-9c70-5f5c2ac427ff"}}]}}}]}}]}}}
121
-        """
122 123
 
123
-        req_result.json.return_value = json.loads(response_str)
124
+        req_result.json.return_value = json.loads(self.response_str)
124 125
         req_result.status_code = 200
125 126
 
126 127
         with mock.patch.object(requests, 'post', return_value=req_result):
127
-            self.dispatcher.get_meter(req, res)
128
+            self.dispatcher.get_meters(req, res)
128 129
 
129 130
         # test that the response code is 200
130 131
         self.assertEqual(res.status, getattr(falcon, 'HTTP_200'))
@@ -145,3 +146,35 @@ class TestMeterDispatcher(base.BaseTestCase):
145 146
             self.dispatcher.post_meters(mock.Mock(), res)
146 147
 
147 148
         self.assertEqual(getattr(falcon, 'HTTP_204'), res.status)
149
+
150
+    def test_get_meter_byname(self):
151
+        res = mock.Mock()
152
+        req = mock.Mock()
153
+
154
+        def _side_effect(arg):
155
+            if arg == 'name':
156
+                return 'tongli'
157
+            elif arg == 'dimensions':
158
+                return 'key1:100, key2:200'
159
+        req.get_param.side_effect = _side_effect
160
+
161
+        req_result = mock.Mock()
162
+
163
+        req_result.json.return_value = json.loads(self.response_str)
164
+        req_result.status_code = 200
165
+
166
+        with mock.patch.object(requests, 'post', return_value=req_result):
167
+            self.dispatcher.get_meter_byname(req, res, "BABMGD")
168
+
169
+        # test that the response code is 200
170
+        self.assertEqual(res.status, getattr(falcon, 'HTTP_200'))
171
+        obj = json.loads(res.body)
172
+        self.assertEqual(obj[0]['counter_name'], 'BABMGD')
173
+        self.assertEqual(obj[0]['counter_type'], 'metrics')
174
+        self.assertEqual(obj[0]['user_id'],
175
+                         'efd87807-12d2-4b38-9c70-5f5c2ac427ff')
176
+        self.assertEqual(obj[0]['project_id'],
177
+                         '35b17138-b364-4e6a-a131-8f3099c5be68')
178
+        self.assertEqual(obj[0]['counter_volume'], 4)
179
+        self.assertEqual(obj[0]['timestamp'], 1461337094000)
180
+        self.assertEqual(len(obj), 1)

+ 81
- 3
kiloeyes/v2/elasticsearch/meters.py View File

@@ -108,6 +108,13 @@ class MeterDispatcher(object):
108 108
         ["dimensions_hash","timestamp","value"]},"size":1}}}}}}}
109 109
         """
110 110
 
111
+        self._oldsample_agg = """
112
+        {"by_name":{"terms":{"field":"name","size":%(size)d},
113
+        "aggs":{"by_dim":{"terms":{"field":"dimensions_hash","size":%(size)d},
114
+        "aggs":{"meters":{"top_hits":{"_source":{"exclude":
115
+        ["dimensions_hash"]},"size":1}}}}}}}
116
+        """
117
+
111 118
         self.setup_index_template()
112 119
 
113 120
     def setup_index_template(self):
@@ -127,9 +134,9 @@ class MeterDispatcher(object):
127 134
 
128 135
     def post_data(self, req, res):
129 136
         msg = ""
130
-        LOG.debug('@$Post Message is %s' % msg)
131 137
         LOG.debug('Getting the call.')
132 138
         msg = req.stream.read()
139
+        LOG.debug('@$Post Message is %s' % msg)
133 140
 
134 141
         code = self._kafka_conn.send_messages(msg)
135 142
         res.status = getattr(falcon, 'HTTP_' + str(code))
@@ -144,7 +151,7 @@ class MeterDispatcher(object):
144 151
             return None
145 152
 
146 153
     @resource_api.Restify('/v2.0/meters', method='get')
147
-    def get_meter(self, req, res):
154
+    def get_meters(self, req, res):
148 155
         LOG.debug('The meters GET request is received')
149 156
 
150 157
         # process query condition
@@ -203,6 +210,77 @@ class MeterDispatcher(object):
203 210
         else:
204 211
             res.body = ''
205 212
 
206
-    @resource_api.Restify('/v2.0/meters/', method='post')
213
+    @resource_api.Restify('/v2.0/meters', method='post')
207 214
     def post_meters(self, req, res):
208 215
         self.post_data(req, res)
216
+
217
+    @resource_api.Restify('/v2.0/meters/{meter_name}', method='get')
218
+    def get_meter_byname(self, req, res, meter_name):
219
+        LOG.debug('The meter %s sample GET request is received' % meter_name)
220
+
221
+        # process query condition
222
+        query = []
223
+        metrics.ParamUtil.common(req, query)
224
+        _meter_ag = self._oldsample_agg % {"size": self.size}
225
+        if query:
226
+            body = ('{"query":{"bool":{"must":' + json.dumps(query) + '}},'
227
+                    '"size":' + str(self.size) + ','
228
+                    '"aggs":' + _meter_ag + '}')
229
+        else:
230
+            body = '{"aggs":' + _meter_ag + '}'
231
+
232
+        # modify the query url to filter out name
233
+        query_url = []
234
+        if meter_name:
235
+            query_url = self._query_url + '&q=name:' + meter_name
236
+        else:
237
+            query_url = self._query_url
238
+        LOG.debug('Request body:' + body)
239
+        LOG.debug('Request url:' + query_url)
240
+        es_res = requests.post(query_url, data=body)
241
+        res.status = getattr(falcon, 'HTTP_%s' % es_res.status_code)
242
+
243
+        LOG.debug('Query to ElasticSearch returned: %s' % es_res.status_code)
244
+        res_data = self._get_agg_response(es_res)
245
+        LOG.debug('@$Result data is %s\n' % res_data)
246
+        if res_data:
247
+            # convert the response into ceilometer meter OldSample format
248
+            aggs = res_data['by_name']['buckets']
249
+            flag = {'is_first': True}
250
+
251
+            def _render_hits(item):
252
+                _type = item['meters']['hits']['hits'][0]['_type']
253
+                _source = item['meters']['hits']['hits'][0]['_source']
254
+                rslt = ('{"counter_name":' + json.dumps(_source['name']) + ','
255
+                        '"counter_type":' + json.dumps(_type) + ','
256
+                        '"counter_unit":null,'
257
+                        '"counter_volume":' +
258
+                        json.dumps(_source['value']) + ','
259
+                        '"message_id":null,'
260
+                        '"project_id":' +
261
+                        json.dumps(_source['project_id']) + ','
262
+                        '"recorded_at":null,'
263
+                        '"resource_id":' +
264
+                        json.dumps(_source['tenant_id']) + ','
265
+                        '"resource_metadata":null,'
266
+                        '"source":' + json.dumps(_source['user_agent']) + ','
267
+                        '"timestamp":' + json.dumps(_source['timestamp']) + ','
268
+                        '"user_id":' + json.dumps(_source['user_id']) + '}')
269
+                if flag['is_first']:
270
+                    flag['is_first'] = False
271
+                    return rslt
272
+                else:
273
+                    return ',' + rslt
274
+
275
+            def _make_body(buckets):
276
+                yield '['
277
+                for by_name in buckets:
278
+                    if by_name['by_dim']:
279
+                        for by_dim in by_name['by_dim']['buckets']:
280
+                            yield _render_hits(by_dim)
281
+                yield ']'
282
+
283
+            res.body = ''.join(_make_body(aggs))
284
+            res.content_type = 'application/json;charset=utf-8'
285
+        else:
286
+            res.body = ''

+ 1
- 0
kiloeyes/v2/elasticsearch/metrics.py View File

@@ -231,6 +231,7 @@ class MetricDispatcher(object):
231 231
     def post_data(self, req, res):
232 232
         LOG.debug('Getting the call.')
233 233
         msg = req.stream.read()
234
+        LOG.debug('@Post: %s' % msg)
234 235
 
235 236
         code = self._kafka_conn.send_messages(msg)
236 237
         res.status = getattr(falcon, 'HTTP_' + str(code))

+ 2
- 2
setup.cfg View File

@@ -49,14 +49,14 @@ kiloeyes.dispatcher =
49 49
     versions = kiloeyes.v2.elasticsearch.versions:VersionDispatcher
50 50
     alarmdefinitions = kiloeyes.v2.elasticsearch.alarmdefinitions:AlarmDefinitionDispatcher
51 51
     notificationmethods = kiloeyes.v2.elasticsearch.notificationmethods:NotificationMethodDispatcher
52
-	alarms = kiloeyes.v2.elasticsearch.alarms:AlarmDispatcher
52
+    alarms = kiloeyes.v2.elasticsearch.alarms:AlarmDispatcher
53 53
     meters = kiloeyes.v2.elasticsearch.meters:MeterDispatcher
54 54
 
55 55
 kiloeyes.index.strategy =
56 56
     timed = kiloeyes.microservice.timed_strategy:TimedStrategy
57 57
     fixed = kiloeyes.microservice.fixed_strategy:FixedStrategy
58 58
 
59
-kiloeyes.message.processor = 
59
+kiloeyes.message.processor =
60 60
     metrics_msg_fixer = kiloeyes.microservice.metrics_fixer:MetricsFixer
61 61
     notification_processor = kiloeyes.microservice.notification_processor:NotificationProcessor
62 62
     threshold_processor = kiloeyes.microservice.threshold_processor:ThresholdProcessor

Loading…
Cancel
Save