Skip to content

Commit b0c9f1b

Browse files
committed
Merge pull request #2 from burmanm/master
Fix to work with current master (0.3.3-SNAPSHOT)
2 parents 54fbe56 + cb4e359 commit b0c9f1b

3 files changed

Lines changed: 89 additions & 76 deletions

File tree

hawkular/metrics.py

Lines changed: 49 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,33 @@
22
import urllib2
33
import urllib
44
import time
5+
import collections
56

67
"""
78
TODO: Remember to do imports for Python 3 also and check the compatibility..
89
TODO: Search datapoints with tags.. tag datapoints.
910
TODO: Allow changing instance's tenant?
1011
TODO: Authentication when it's done..
1112
TODO: Remove HawkularMetricsConnectionError and use HawkularMetricsError only?
12-
TODO: 0.3.3 will change /hawkular-metrics to hawkular/metrics and the REST-interfaces
13+
TODO: 0.3.3 will before release move the tenantId to headers..
1314
"""
1415

1516
class MetricType:
16-
Numeric = 'numeric'
17+
Gauge = 'gauges'
1718
Availability = 'availability'
19+
Counter = 'counters'
1820

1921
@staticmethod
2022
def short(metric_type):
21-
if metric_type is 'numeric':
22-
return 'num'
23+
if metric_type is MetricType.Gauge:
24+
return 'gauge'
2325
else:
24-
return 'avail'
26+
return 'availability'
2527

2628
class Availability:
2729
Down = 'down'
2830
Up = 'up'
31+
Unknown = 'unknown'
2932

3033
class HawkularMetricsError(urllib2.HTTPError):
3134
pass
@@ -53,8 +56,8 @@ class HawkularMetricsClient:
5356
def __init__(self,
5457
tenant_id,
5558
host='localhost',
56-
port=8081,
57-
path='hawkular-metrics'):
59+
port=8080,
60+
path='hawkular/metrics'):
5861
"""
5962
A new instance of HawkularMetricsClient is created with the following defaults:
6063
@@ -84,14 +87,11 @@ def _clean_metric_id(metric_id):
8487
def _get_base_url(self):
8588
return "http://{0}:{1}/{2}/".format(self.host, str(self.port), self.path)
8689

87-
def _get_url(self, service):
88-
return self._get_base_url() + '{0}/{1}'.format(self.tenant_id, service)
89-
90-
def _get_metrics_url(self, metric_type):
91-
return self._get_url('metrics') + "/{0}".format(metric_type)
90+
def _get_url(self, metric_type):
91+
return self._get_base_url() + '{0}/{1}'.format(self.tenant_id, metric_type)
9292

9393
def _get_metrics_single_url(self, metric_type, metric_id):
94-
return self._get_metrics_url(metric_type) + '/{0}'.format(self._clean_metric_id(metric_id))
94+
return self._get_url(metric_type) + '/{0}'.format(self._clean_metric_id(metric_id))
9595

9696
def _get_metrics_data_url(self, metrics_url):
9797
return metrics_url + '/data'
@@ -185,17 +185,27 @@ def _isfloat(value):
185185
Instance methods
186186
"""
187187

188-
def put(self, metric_type, data):
188+
def put(self, data):
189189
"""
190-
Send multiple different metric_ids to the server in a single
191-
batch.
190+
Send multiple different metric_ids to the server in a single batch. Metrics can be a mixture
191+
of types.
192192
193-
data is a dict or a list of dicts created with create_metric(metric_id, metric_dict)
193+
data is a dict or a list of dicts created with create_metric(metric_type, metric_id, datapoints)
194194
"""
195195
if not isinstance(data, list):
196196
data = [data]
197-
198-
self._post(self._get_metrics_data_url(self._get_metrics_url(metric_type)), data)
197+
198+
r = collections.defaultdict(list)
199+
200+
for d in data:
201+
metric_type = d.pop('type', None)
202+
if metric_type is None:
203+
raise HawkularMetricsError('Undefined MetricType')
204+
r[metric_type].append(d)
205+
206+
# This isn't transactional, but .. ouh well. One can always repost everything.
207+
for l in r:
208+
self._post(self._get_metrics_data_url(self._get_url(l)), r[l])
199209

200210
def push(self, metric_type, metric_id, value, timestamp=None, **tags):
201211
"""
@@ -204,8 +214,8 @@ def push(self, metric_type, metric_id, value, timestamp=None, **tags):
204214
This method is an assistant method for the put method by removing the need to
205215
create data structures first.
206216
"""
207-
item = create_metric(metric_id, create_datapoint(value, timestamp, **tags))
208-
self.put(metric_type, item)
217+
item = create_metric(metric_type, metric_id, create_datapoint(value, timestamp, **tags))
218+
self.put(item)
209219

210220
def query_metric(self, metric_type, metric_id, **search_options):
211221
"""
@@ -224,7 +234,7 @@ def query_single_numeric(self, metric_id, **search_options):
224234
"""
225235
See query_metric
226236
"""
227-
return self.query_metric(MetricType.Numeric, metric_id, **search_options)
237+
return self.query_metric(MetricType.Gauge, metric_id, **search_options)
228238

229239
def query_single_availability(self, metric_id, **search_options):
230240
"""
@@ -234,12 +244,12 @@ def query_single_availability(self, metric_id, **search_options):
234244

235245
def query_definitions(self, query_type):
236246
"""
237-
Query available metric definitions. Use 'avail' or 'num' or MetricType.Availability / MetricType.Numeric
247+
Query available metric definitions.
238248
"""
239-
if isinstance(query_type, MetricType):
240-
query_type = MetricType.short(query_type)
249+
# if isinstance(query_type, MetricType):
250+
# query_type = MetricType.short(query_type)
241251

242-
definition_url = self._get_url('metrics') + '?type=' + MetricType.short(query_type)
252+
definition_url = self._get_url('metrics') + '?type=' + query_type
243253
return self._get(definition_url)
244254

245255
def create_metric_definition(self, metric_type, metric_id, **tags):
@@ -248,7 +258,7 @@ def create_metric_definition(self, metric_type, metric_id, **tags):
248258
units, env ..
249259
250260
Use methods create_numeric_definition and create_availability_definition to avoid using
251-
MetricType.Numeric / MetricType.Availability
261+
MetricType.Gauge / MetricType.Availability
252262
"""
253263
item = { 'id': metric_id }
254264
if len(tags) > 0:
@@ -261,19 +271,26 @@ def create_metric_definition(self, metric_type, metric_id, **tags):
261271
item['tags'] = tags
262272

263273
json_data = json.dumps(item, indent=2)
264-
self._post(self._get_metrics_url(metric_type), json_data)
274+
try:
275+
self._post(self._get_url(metric_type), json_data)
276+
except HawkularMetricsError as e:
277+
if e.code == 409:
278+
return False
279+
raise e
280+
281+
return True
265282

266283
def create_numeric_definition(self, metric_id, **tags):
267284
"""
268285
See create_metric_definition
269286
"""
270-
self.create_metric_definition(MetricType.Numeric, metric_id, **tags)
287+
return self.create_metric_definition(MetricType.Gauge, metric_id, **tags)
271288

272289
def create_availability_definition(self, metric_id, **tags):
273290
"""
274291
See create_metric_definition
275292
"""
276-
self.create_metric_definition(MetricType.Availability, metric_id, **tags)
293+
return self.create_metric_definition(MetricType.Availability, metric_id, **tags)
277294

278295
def query_metric_tags(self, metric_type, metric_id):
279296
"""
@@ -348,12 +365,12 @@ def create_datapoint(value, timestamp=None, **tags):
348365

349366
return item
350367

351-
def create_metric(metric_id, data):
368+
def create_metric(metric_type, metric_id, data):
352369
"""
353370
Create Hawkular-Metrics' submittable structure, data is a datapoint or list of datapoints
354371
"""
355372
if not isinstance(data, list):
356373
data = [data]
357374

358-
return { 'id': metric_id, 'data': data }
375+
return { 'type': metric_type,'id': metric_id, 'data': data }
359376

hawkular/metrics_test.py

Lines changed: 39 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import unittest
22
import uuid
3-
# import metrics
4-
# from metrics import Availability, MetricType, HawkularMetricsError
53
from metrics import *
64

75
class TestMetricFunctionsBase(unittest.TestCase):
@@ -48,12 +46,15 @@ def test_numeric_creation(self):
4846
Test creating numeric metric definitions with different tags and definition.
4947
"""
5048
# Create numeric metrics with empty details and added details
51-
self.client.create_numeric_definition('test.create.numeric.1')
52-
self.client.create_numeric_definition('test.create.numeric.2', dataRetention=90)
53-
self.client.create_numeric_definition('test.create.numeric.3', dataRetention=90, units='bytes', env='qa')
49+
md1 = self.client.create_numeric_definition('test.create.numeric.1')
50+
md2 = self.client.create_numeric_definition('test.create.numeric.2', dataRetention=90)
51+
md3 = self.client.create_numeric_definition('test.create.numeric.3', dataRetention=90, units='bytes', env='qa')
52+
self.assertTrue(md1)
53+
self.assertTrue(md2)
54+
self.assertTrue(md3)
5455

5556
# Fetch metrics definition and check that the ones we created appeared also
56-
m = self.client.query_definitions(MetricType.Numeric)
57+
m = self.client.query_definitions(MetricType.Gauge)
5758
self.assertEqual(3, len(m))
5859
self.assertEqual(self.test_tenant, m[0]['tenantId'])
5960
self.assertEqual('bytes', m[2]['tags']['units'])
@@ -69,13 +70,8 @@ def test_numeric_creation(self):
6970
self.assertEqual(m, expect) # Did it?
7071

7172
# Lets try creating a duplicate metric
72-
try:
73-
self.client.create_numeric_definition('test.create.numeric.1')
74-
self.fail('Should have received an exception, metric with the same name was already created')
75-
except HawkularMetricsError, e:
76-
# Check return code 400 and that the failure message was correctly parsed
77-
self.assertEqual(409, e.code)
78-
self.assertEqual('A metric with name [test.create.numeric.1] already exists', e.msg)
73+
md4 = self.client.create_numeric_definition('test.create.numeric.1')
74+
self.assertFalse(md4, 'Should have received an exception, metric with the same name was already created')
7975

8076
def test_availability_creation(self):
8177
# Create availability metric
@@ -92,19 +88,19 @@ def test_tags_modifications(self):
9288
m = 'test.create.tags.1'
9389
# Create metric without tags
9490
self.client.create_numeric_definition(m)
95-
e = self.client.query_metric_tags(MetricType.Numeric, m)
91+
e = self.client.query_metric_tags(MetricType.Gauge, m)
9692
self.assertIsNotNone(e)
9793
self.assertEqual({}, e)
9894
# Add tags
99-
self.client.update_metric_tags(MetricType.Numeric, m, hostname='machine1', a='b')
95+
self.client.update_metric_tags(MetricType.Gauge, m, hostname='machine1', a='b')
10096
# Fetch metric - check for tags
101-
tags = self.client.query_metric_tags(MetricType.Numeric, m)
97+
tags = self.client.query_metric_tags(MetricType.Gauge, m)
10298
self.assertEqual(2, len(tags))
10399
self.assertEqual("b", tags['a'])
104100
# Delete some metric tags
105-
self.client.delete_metric_tags(MetricType.Numeric, m, a='b', hostname='machine1')
101+
self.client.delete_metric_tags(MetricType.Gauge, m, a='b', hostname='machine1')
106102
# Fetch metric - check that tags were deleted
107-
tags_2 = self.client.query_metric_tags(MetricType.Numeric, m)
103+
tags_2 = self.client.query_metric_tags(MetricType.Gauge, m)
108104
self.assertEqual(0, len(tags_2))
109105

110106
# def test_tags_behavior(self):
@@ -120,19 +116,18 @@ def test_tags_modifications(self):
120116
# print 'END: TEST TAGS'
121117

122118
def test_add_numeric_single(self):
123-
124119
# Normal way
125120
value = float(4.35)
126121
datapoint = create_datapoint(value, time_millis())
127-
metric = create_metric('test.numeric./', datapoint)
128-
self.client.put(MetricType.Numeric, metric)
122+
metric = create_metric(MetricType.Gauge, 'test.numeric./', datapoint)
123+
self.client.put(metric)
129124

130125
# Fetch results
131126
data = self.client.query_single_numeric('test.numeric./')
132127
self.assertEqual(float(data[0]['value']), value)
133128

134129
# Shortcut method with tags
135-
self.client.push(MetricType.Numeric, 'test.numeric.single.tags', value, hostname='localhost')
130+
self.client.push(MetricType.Gauge, 'test.numeric.single.tags', value, hostname='localhost')
136131

137132
# Fetch results
138133
data = self.client.query_single_numeric('test.numeric.single.tags')
@@ -153,41 +148,42 @@ def test_add_numeric_multi_datapoint(self):
153148
metric_1v = create_datapoint(float(1.45))
154149
metric_2v = create_datapoint(float(2.00), (time_millis() - 2000))
155150

156-
metric = create_metric('test.numeric.multi', [metric_1v, metric_2v])
157-
self.client.put(MetricType.Numeric, metric)
151+
metric = create_metric(MetricType.Gauge, 'test.numeric.multi', [metric_1v, metric_2v])
152+
self.client.put(metric)
158153

159154
data = self.client.query_single_numeric('test.numeric.multi')
160155
self.assertEqual(len(data), 2)
161156
self.assertEqual(data[0]['value'], float(1.45))
162157
self.assertEqual(data[1]['value'], float(2.00))
163158

164159
def test_add_availability_multi_datapoint(self):
165-
up = create_datapoint('up', (time_millis() - 2000))
166-
down = create_datapoint('down')
160+
t = time_millis()
161+
up = create_datapoint('up', (t - 2000))
162+
down = create_datapoint('down', t)
167163

168-
m = create_metric('test.avail.multi', [up, down])
164+
m = create_metric(MetricType.Availability, 'test.avail.multi', [up, down])
169165

170-
self.client.put(MetricType.Availability, m)
166+
self.client.put(m)
171167
data = self.client.query_single_availability('test.avail.multi')
172168

173169
self.assertEqual(len(data), 2)
174-
self.assertEqual(data[0]['value'], 'down')
175-
self.assertEqual(data[1]['value'], 'up')
170+
self.assertEqual(data[0]['value'], 'up')
171+
self.assertEqual(data[1]['value'], 'down')
176172

177-
def test_add_multi_metrics_and_datapoints(self):
173+
def test_add_mixed_metrics_and_datapoints(self):
178174
metric1 = create_datapoint(float(1.45))
179175
metric1_2 = create_datapoint(float(2.00), (time_millis() - 2000))
180176

181-
metric_multi = create_metric('test.multi.numeric.1', [metric1, metric1_2])
177+
metric_multi = create_metric(MetricType.Gauge, 'test.multi.numeric.1', [metric1, metric1_2])
182178

183-
metric2 = create_datapoint(float(1.55))
184-
metric2_multi = create_metric('test.multi.numeric.2', [metric2])
179+
metric2 = create_datapoint(Availability.Up)
180+
metric2_multi = create_metric(MetricType.Availability,'test.multi.numeric.2', [metric2])
185181

186-
self.client.put(MetricType.Numeric, [metric_multi, metric2_multi])
182+
self.client.put([metric_multi, metric2_multi])
187183

188184
# Check that both were added correctly..
189185
metric1_data = self.client.query_single_numeric('test.multi.numeric.1')
190-
metric2_data = self.client.query_single_numeric('test.multi.numeric.2')
186+
metric2_data = self.client.query_single_availability('test.multi.numeric.2')
191187

192188
self.assertEqual(2, len(metric1_data))
193189
self.assertEqual(1, len(metric2_data))
@@ -198,29 +194,29 @@ def test_query_options(self):
198194
v1 = create_datapoint(float(1.45), t)
199195
v2 = create_datapoint(float(2.00), (t - 2000))
200196

201-
m = create_metric('test.query.numeric.1', [v1, v2])
202-
self.client.put(MetricType.Numeric, m)
197+
m = create_metric(MetricType.Gauge, 'test.query.numeric.1', [v1, v2])
198+
self.client.put(m)
203199

204200
# Query first without limitations
205-
d = self.client.query_metric(MetricType.Numeric, 'test.query.numeric.1')
201+
d = self.client.query_metric(MetricType.Gauge, 'test.query.numeric.1')
206202
self.assertEqual(2, len(d))
207203

208204
# Query for data which has start time limitation
209-
d = self.client.query_metric(MetricType.Numeric, 'test.query.numeric.1', start=(t-1000))
205+
d = self.client.query_metric(MetricType.Gauge, 'test.query.numeric.1', start=(t-1000))
210206
self.assertEqual(1, len(d))
211207

212208
# This feature isn't really ready for prime time in Hawkular-Metrics yet..
213209
# def test_tags_finding(self):
214210
# # Create metrics with tags
215211
# m = 'test.create.data.tags.1'
216-
# self.client.create_metric_definition(MetricType.Numeric, m, ab='cd')
212+
# self.client.create_metric_definition(MetricType.Gauge, m, ab='cd')
217213
# # Push some data to them
218214
# t = time_millis()
219215
# v = float(1.4)
220-
# self.client.push(MetricType.Numeric, m, v, t)
216+
# self.client.push(MetricType.Gauge, m, v, t)
221217
# # Fetch data with certain tags
222218
# expected = { 'id': m, 'timestamp': t, 'value': v }
223-
# d = self.client.query_data_with_tags(MetricType.Numeric, ab='cd')
219+
# d = self.client.query_data_with_tags(MetricType.Gauge, ab='cd')
224220
# self.assertIsNotNone(d)
225221
# self.assertIn(expected, d)
226222

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from distutils.core import setup
44

55
setup(name='hawkular-client-python',
6-
version='0.3.2',
6+
version='0.3.3',
77
description='Python client to communicate with Hawkular over HTTP',
88
author='Michael Burman',
99
author_email='miburman@redhat.com',

0 commit comments

Comments
 (0)