hawkular-client 0.1.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -3
  3. data/CHANGES.rdoc +8 -0
  4. data/README.rdoc +12 -5
  5. data/hawkularclient.gemspec +2 -2
  6. data/lib/alerts/alerts_api.rb +193 -0
  7. data/lib/{hawkularclient.rb → hawkular.rb} +93 -52
  8. data/lib/hawkular_all.rb +7 -0
  9. data/lib/inventory/inventory_api.rb +378 -0
  10. data/lib/metrics/metric_api.rb +20 -10
  11. data/lib/metrics/metrics_client.rb +52 -0
  12. data/lib/metrics/tenant_api.rb +1 -1
  13. data/lib/metrics/version.rb +3 -1
  14. data/lib/version.rb +8 -0
  15. data/spec/integration/alerts_spec.rb +143 -0
  16. data/spec/integration/inventory_spec.rb +191 -0
  17. data/spec/integration/metric_spec.rb +33 -0
  18. data/spec/spec_helper.rb +14 -1
  19. data/spec/unit/base_spec.rb +57 -0
  20. data/spec/vcr_cassettes/Alert/Alerts/Should_acknowledge_an_alert.yml +183 -0
  21. data/spec/vcr_cassettes/Alert/Alerts/Should_fetch_single_alert.yml +69 -0
  22. data/spec/vcr_cassettes/Alert/Alerts/Should_list_alerts.yml +85 -0
  23. data/spec/vcr_cassettes/Alert/Alerts/Should_list_alerts_for_trigger.yml +142 -0
  24. data/spec/vcr_cassettes/Alert/Alerts/Should_list_alerts_for_unknown_trigger.yml +46 -0
  25. data/spec/vcr_cassettes/Alert/Alerts/Should_resolve_an_alert.yml +181 -0
  26. data/spec/vcr_cassettes/Alert/Alerts/Should_resolve_an_alert2.yml +49 -0
  27. data/spec/vcr_cassettes/Alert/Triggers/Should_List_Triggers.yml +62 -0
  28. data/spec/vcr_cassettes/Alert/Triggers/Should_List_Triggers_for_ID.yml +55 -0
  29. data/spec/vcr_cassettes/Alert/Triggers/Should_List_Triggers_for_Tag.yml +68 -0
  30. data/spec/vcr_cassettes/Alert/Triggers/Should_List_Triggers_for_Tags.yml +68 -0
  31. data/spec/vcr_cassettes/Alert/Triggers/Should_get_a_single_Trigger_with_conditions.yml +138 -0
  32. data/spec/vcr_cassettes/Alert/Triggers/Should_get_a_single_metric_Trigger.yml +50 -0
  33. data/spec/vcr_cassettes/Gauge_metrics/Platform_mem.yml +44 -0
  34. data/spec/vcr_cassettes/Gauge_metrics/Platform_mem_def.yml +45 -0
  35. data/spec/vcr_cassettes/Inventory/Should_List_datasources_with_no_props.yml +228 -0
  36. data/spec/vcr_cassettes/Inventory/Should_list_URLs.yml +105 -0
  37. data/spec/vcr_cassettes/Inventory/Should_list_WildFlys.yml +104 -0
  38. data/spec/vcr_cassettes/Inventory/Should_list_WildFlys_with_props.yml +162 -0
  39. data/spec/vcr_cassettes/Inventory/Should_list_children_of_WildFly.yml +180 -0
  40. data/spec/vcr_cassettes/Inventory/Should_list_feeds.yml +102 -0
  41. data/spec/vcr_cassettes/Inventory/Should_list_heap_metrics_for_WildFlys.yml +308 -0
  42. data/spec/vcr_cassettes/Inventory/Should_list_metrics_for_WildFlys.yml +172 -0
  43. data/spec/vcr_cassettes/Inventory/Should_list_types_with_bad_feed.yml +101 -0
  44. data/spec/vcr_cassettes/Inventory/Should_list_types_with_feed.yml +109 -0
  45. data/spec/vcr_cassettes/Inventory/Should_list_types_without_feed.yml +110 -0
  46. data/spec/vcr_cassettes/Metrics/Status.yml +44 -0
  47. data/spec/vcr_cassettes/No_Tenant/Should_fail.yml +42 -0
  48. data/spec/vcr_cassettes/Tenants/Should_Get_Tenant_For_Explicit_Credentials.yml +50 -0
  49. data/spec/vcr_cassettes/Tenants/Should_Get_Tenant_For_Implicit_Credentials.yml +50 -0
  50. metadata +74 -3
@@ -0,0 +1,7 @@
1
+ require 'inventory/inventory_api'
2
+ require 'metrics/metrics_client.rb'
3
+ require 'alerts/alerts_api'
4
+ require 'hawkular'
5
+
6
+ module Hawkular
7
+ end
@@ -0,0 +1,378 @@
1
+ require 'hawkular'
2
+
3
+ # Inventory module provides access to the Hawkular Inventory REST API.
4
+ # @see http://www.hawkular.org/docs/rest/rest-inventory.html
5
+ #
6
+ # @note While Inventory supports 'environments', they are not used currently
7
+ # and thus set to 'test' as default value.
8
+ module Hawkular::Inventory
9
+ # Client class to interact with Hawkular Inventory
10
+ class InventoryClient < Hawkular::BaseClient
11
+ # Create a new Inventory Client
12
+ # @param entrypoint [String] base url of Hawkular-inventory - e.g
13
+ # http://localhost:8080/hawkular/inventory
14
+ # @param credentials [Hash{String=>String}] Hash of username, password, token(optional)
15
+ def initialize(entrypoint = nil, credentials = {})
16
+ @entrypoint = entrypoint
17
+
18
+ super(entrypoint, credentials)
19
+ end
20
+
21
+ # Retrieve the tenant id for the passed credentials.
22
+ # If no credentials are passed, the ones from the constructor are used
23
+ # @param credentials [Hash{String=>String}] Hash of username, password, token(optional)
24
+ # @return [String] tenant id
25
+ def get_tenant(credentials = {})
26
+ creds = credentials.empty? ? @credentials : credentials
27
+ auth_header = { Authorization: base_64_credentials(creds) }
28
+
29
+ ret = http_get('/tenant', auth_header)
30
+
31
+ ret['id']
32
+ end
33
+
34
+ # TODO: revisit and potentially move to Base ?
35
+ def impersonate(credentials = {})
36
+ @tenant = get_tenant(credentials)
37
+ @options[:tenant] = @tenant
38
+ end
39
+
40
+ # List feeds in the system
41
+ # @return [Array<String>] List of feed ids
42
+ def list_feeds(_environment = 'test')
43
+ ret = http_get('feeds')
44
+ val = []
45
+ ret.each { |f| val.push(f['id']) }
46
+ val
47
+ end
48
+
49
+ # List resource types. If no need is given all types are listed
50
+ # @param [String] feed The id of the feed the type lives under. Can be nil for feedless types
51
+ # @return [Array<ResourceType>] List of types, that can be empty
52
+ def list_resource_types(feed = nil)
53
+ if feed.nil?
54
+ ret = http_get('/resourceTypes')
55
+ else
56
+ the_feed = hawk_escape feed
57
+ ret = http_get('/feeds/' + the_feed + '/resourceTypes')
58
+ end
59
+ val = []
60
+ ret.each { |rt| val.push(ResourceType.new(rt)) }
61
+ val
62
+ end
63
+
64
+ # List the resources for the passed feed and resource type. The representation for
65
+ # resources under a feed are sparse and additional data must be retrived separately.
66
+ # It is possible though to also obtain runtime properties by setting #fetch_properties to true.
67
+ # @param [String] feed The id of the feed the type lives under. Can be nil for feedless types
68
+ # @param [String] type Name of the type to look for. Can be obtained from {ResourceType}.id.
69
+ # Must not be nil
70
+ # @param [Boolean] fetch_properties Shall additional runtime properties be fetched?
71
+ # @return [Array<Resource>] List of resources. Can be empty
72
+ def list_resources_for_type(feed, type, fetch_properties = false)
73
+ fail 'Type must not be nil' unless type
74
+ the_type = hawk_escape type
75
+ if feed.nil?
76
+ ret = http_get('resourceTypes/' + the_type + '/resources')
77
+ else
78
+
79
+ the_feed = hawk_escape feed
80
+ ret = http_get('/feeds/' + the_feed + '/resourceTypes/' + the_type + '/resources')
81
+ end
82
+ val = []
83
+ ret.each do |r|
84
+ if fetch_properties && !feed.nil?
85
+ p = get_config_data_for_resource(r['id'], feed)
86
+ r['properties'] = p['value']
87
+ end
88
+ val.push(Resource.new(r))
89
+ end
90
+ val
91
+ end
92
+
93
+ # Retrieve runtime properties for the passed resource
94
+ # @param [String] resource_id Id of the resource to read properties from
95
+ # @param [String] feed Feed of the resource
96
+ # @return [Hash<String,Object] Hash with additional data
97
+ def get_config_data_for_resource(resource_id, feed)
98
+ the_id = hawk_escape resource_id
99
+ the_feed = hawk_escape feed
100
+ http_get('feeds/' + the_feed + '/resources/' + the_id + '/data?dataType=configuration')
101
+ rescue
102
+ {}
103
+ end
104
+
105
+ # Obtain the child resources of the passed resource. In case of a WildFly server,
106
+ # those would be Datasources, Deployments and so on.
107
+ # @param [Resource] parent_resource Resource to obtain children from
108
+ # @return [Array<Resource>] List of resources that are children of the given parent resource.
109
+ # Can be empty
110
+ def list_child_resources(parent_resource)
111
+ the_feed = hawk_escape parent_resource.feed
112
+ the_id = hawk_escape parent_resource.id
113
+
114
+ ret = http_get('/feeds/' + the_feed +
115
+ '/resources/' + the_id + '/children')
116
+ val = []
117
+ ret.each { |r| val.push(Resource.new(r)) }
118
+ val
119
+ end
120
+
121
+ # Obtain a list of relationships starting at the passed resource
122
+ # @param [Resource] resource One end of the relationship
123
+ # @return [Array<Relationship>] List of relationships
124
+ def list_relationships(resource)
125
+ the_feed = hawk_escape resource.feed
126
+ the_id = hawk_escape resource.id
127
+
128
+ ret = http_get('/feeds/' + the_feed + '/resources/' + the_id + '/relationships')
129
+ val = []
130
+ ret.each { |r| val.push(Relationship.new(r)) }
131
+ val
132
+ rescue
133
+ []
134
+ end
135
+
136
+ # Obtain a list of relationships for the passed feed
137
+ # @param [String] feed_id Id of the feed
138
+ # @return [Array<Relationship>] List of relationships
139
+ def list_relationships_for_feed(feed_id)
140
+ the_feed = hawk_escape feed_id
141
+ ret = http_get('/feeds/' + the_feed + '/relationships')
142
+ val = []
143
+ ret.each { |r| val.push(Relationship.new(r)) }
144
+ val
145
+ rescue
146
+ []
147
+ end
148
+
149
+ # [15:01:51] <jkremser> pilhuhn, this works for me curl -XPOST
150
+ # -H "Content-Type: application/json"
151
+ # -u jdoe:password -d
152
+ # '{"id" : "foo", "source": "/t;28026b36-8fe4-4332-84c8-524e173a68bf/f;localhost",
153
+ # "target": "/t;28026b36-8fe4-4332-84c8-524e173a68bf/f;localhost/r;localhost~Local~~/
154
+ # r;localhost~Local~%2Fsubsystem=hawkular-bus-broker",
155
+ # "name": "isRelatedTo"}'
156
+ # 'http://localhost:8080/hawkular/inventory/feeds/localhost/relationships'
157
+ #
158
+ # def create_relationship(source_resource, target_resource, name, properties = {})
159
+ # rel = Relationship.new
160
+ # rel.source_id = source_resource.path
161
+ # rel.target_id = target_resource.path
162
+ # rel.name = name
163
+ # rel.properties = properties
164
+ #
165
+ # http_post('/feeds/' + source_resource.feed + '/relationships',
166
+ # rel.to_h)
167
+ # end
168
+
169
+ # def list_metrics_for_resource_type
170
+ # # TODO implement me
171
+ # end
172
+
173
+ # List metric (definitions) for the passed resource. It is possible to filter down the
174
+ # result by a filter to only return a subset. The
175
+ # @param [Resource] resource
176
+ # @param [Hash{Symbol=>String}] filter for 'type' and 'match'
177
+ # Metric type can be one of 'GAUGE', 'COUNTER', 'AVAILABILITY'. If a key is missing
178
+ # it will not be used for filtering
179
+ # @return [Aray<Metric>] List of metrics that can be empty.
180
+ # @example
181
+ # # Filter by type and match on metrics id
182
+ # client.list_metrics_for_resource(wild_fly, type: 'GAUGE', match: 'Metrics~Heap')
183
+ # # Filter by type only
184
+ # client.list_metrics_for_resource(wild_fly, type: 'COUNTER')
185
+ # # Don't filter, return all metric definitions
186
+ # client.list_metrics_for_resource(wild_fly)
187
+ def list_metrics_for_resource(resource, filter = {})
188
+ the_feed = hawk_escape resource.feed
189
+ the_id = hawk_escape resource.id
190
+
191
+ ret = http_get('/feeds/' +
192
+ the_feed + '/resources/' +
193
+ the_id + '/metrics')
194
+ val = []
195
+ ret.each do |m|
196
+ metric_new = Metric.new(m)
197
+ found = should_include?(metric_new, filter)
198
+ val.push(metric_new) if found
199
+ end
200
+ val
201
+ end
202
+
203
+ private
204
+
205
+ def should_include?(metric_new, filter)
206
+ found = true
207
+ if filter.empty?
208
+ found = true
209
+ else
210
+ found = false unless filter[:type] == (metric_new.type) || filter[:type].nil?
211
+ found = false unless filter[:match].nil? || metric_new.id.include?(filter[:match])
212
+ end
213
+ found
214
+ end
215
+ end
216
+
217
+ # A ResourceType is like a class definition for {Resource}s
218
+ # ResourceTypes are currently unique per feed, but one can assume
219
+ # that a two types with the same name of two different feeds are
220
+ # (more or less) the same.
221
+ class ResourceType
222
+ # @return [String] Full path of the type
223
+ attr_reader :path
224
+ # @return [String] Name of the type
225
+ attr_reader :name
226
+ # @return [String] Name of the type
227
+ attr_reader :id
228
+ # @return [String] Feed this type belongs to
229
+ attr_reader :feed
230
+ # @return [String] Environment this Type belongs to - currently unused
231
+ attr_reader :env
232
+ # @return [String] Properties of this type
233
+ attr_reader :properties
234
+
235
+ def initialize(rt_hash)
236
+ @id = rt_hash['id']
237
+ @path = rt_hash['path']
238
+ @name = rt_hash['name'] || rt_hash['id']
239
+ @properties = rt_hash['properties']
240
+ @_hash = rt_hash.dup
241
+
242
+ tmp = path.split('/')
243
+ tmp.each do |pair|
244
+ (key, val) = pair.split(';')
245
+ case key
246
+ when 'f'
247
+ @feed = val
248
+ when 'e'
249
+ @env = val
250
+ end
251
+ end
252
+ end
253
+
254
+ # Returns a hash representation of the resource type
255
+ # @return [Hash<String,Object>] hash of the type
256
+ def to_h
257
+ @_hash.dup
258
+ end
259
+ end
260
+
261
+ # A Resource is an instantiation of a {ResourceType}
262
+ class Resource
263
+ # @return [String] Full path of the resource including feed id
264
+ attr_reader :path
265
+ # @return [String] Name of the resource
266
+ attr_reader :name
267
+ # @return [String] Name of the resource
268
+ attr_reader :id
269
+ # @return [String] Name of the feed for this resource
270
+ attr_reader :feed
271
+ # @return [String] Name of the environment for this resource -- currently unused
272
+ attr_reader :env
273
+ # @return [String] Full path of the {ResourceType}
274
+ attr_reader :type_path
275
+ # @return [Hash<String,Object>] Hash with additional, resource specific properties
276
+ attr_reader :properties
277
+
278
+ def initialize(res_hash)
279
+ @id = res_hash['id']
280
+ @path = res_hash['path']
281
+ @properties = res_hash['properties'] || {}
282
+ @type_path = res_hash['type']['path']
283
+ @_hash = res_hash
284
+
285
+ tmp = @path.split('/')
286
+ tmp.each do |pair|
287
+ (key, val) = pair.split(';')
288
+ case key
289
+ when 'f'
290
+ @feed = val
291
+ when 'e'
292
+ @env = val
293
+ when 'n'
294
+ @name = val.nil? ? id : val
295
+ end
296
+ end
297
+ self
298
+ end
299
+
300
+ def to_h
301
+ @_hash.deep_dup
302
+ end
303
+ end
304
+
305
+ # Definition of a Metric inside the inventory.
306
+ class Metric
307
+ # @return [String] Full path of the metric (definition)
308
+ attr_reader :path
309
+ # @return [String] Name of the metric
310
+ attr_reader :name
311
+ attr_reader :id
312
+ attr_reader :feed
313
+ attr_reader :env
314
+ attr_reader :type
315
+ attr_reader :unit
316
+ # @return [Long] collection interval in seconds
317
+ attr_reader :collection_interval
318
+
319
+ def initialize(metric_hash)
320
+ @id = metric_hash['id']
321
+ @path = metric_hash['path']
322
+ @name = metric_hash['name'] || @id
323
+ @_hash = metric_hash.dup
324
+
325
+ tmp = path.split('/')
326
+ tmp.each do |pair|
327
+ (key, val) = pair.split(';')
328
+ case key
329
+ when 'f'
330
+ @feed = val
331
+ when 'e'
332
+ @env = val
333
+ when 'n'
334
+ @name = val.nil? ? id : val
335
+ end
336
+ end
337
+ @type = metric_hash['type']['type']
338
+ @unit = metric_hash['type']['unit']
339
+ @collection_interval = metric_hash['collectionInterval']
340
+ end
341
+
342
+ def to_h
343
+ @_hash.dup
344
+ end
345
+ end
346
+
347
+ # Definition of a Relationship between two entities in Inventory
348
+ class Relationship
349
+ attr_accessor :source_id
350
+ attr_reader :target_id
351
+ attr_reader :properties
352
+ attr_reader :name
353
+ attr_reader :id
354
+
355
+ def initialize(hash = {})
356
+ if hash.empty?
357
+ @properties = {}
358
+ return
359
+ end
360
+
361
+ @source_id = hash['source']
362
+ @target_id = hash['target']
363
+ @properties = hash['properties']
364
+ @name = hash['name']
365
+ @id = hash['id']
366
+ end
367
+
368
+ def to_h
369
+ hash = {}
370
+ hash['source'] = @source_id
371
+ hash['target'] = @target_id
372
+ hash['properties'] = @properties
373
+ hash['name'] = @name
374
+ hash['id'] = @id
375
+ hash
376
+ end
377
+ end
378
+ end
@@ -1,6 +1,7 @@
1
1
  require 'erb'
2
2
 
3
3
  module Hawkular::Metrics
4
+ # Client to access the Hawkular_metrics subsystem
4
5
  class Client
5
6
  # @!visibility private
6
7
  def default_timestamp(array)
@@ -11,6 +12,13 @@ module Hawkular::Metrics
11
12
  array
12
13
  end
13
14
 
15
+ # Return version and status information for the used version of Hawkular-Metrics
16
+ # @return [Hash{String=>String}]
17
+ # ('Implementation-Version', 'Built-From-Git-SHA1', 'Status')
18
+ def fetch_version_and_status
19
+ http_get('/status')
20
+ end
21
+
14
22
  # Push data for multiple metrics of all supported types
15
23
  # @param gauges [Array]
16
24
  # @param counters [Array]
@@ -60,8 +68,9 @@ module Hawkular::Metrics
60
68
  # Query metric definitions by tags
61
69
  # @param tags [Hash]
62
70
  # @return [Array[MetricDefinition]]
63
- def query(tags)
64
- @client.http_get("/metrics/?type=#{@type}&tags=#{tags_param(tags)}").map do |g|
71
+ def query(tags = nil)
72
+ tags_filter = tags.nil? ? '' : "&tags=#{tags_param(tags)}"
73
+ @client.http_get("/metrics/?type=#{@type}#{tags_filter}").map do |g|
65
74
  Hawkular::Metrics::MetricDefinition.new(g)
66
75
  end
67
76
  end
@@ -70,7 +79,8 @@ module Hawkular::Metrics
70
79
  # @param id [String]
71
80
  # @return [MetricDefinition]
72
81
  def get(id)
73
- Hawkular::Metrics::MetricDefinition.new(@client.http_get("/#{@resource}/#{id}"))
82
+ the_id = @client.hawk_escape id
83
+ Hawkular::Metrics::MetricDefinition.new(@client.http_get("/#{@resource}/#{the_id}"))
74
84
  end
75
85
 
76
86
  # update tags for given metric definition
@@ -106,8 +116,8 @@ module Hawkular::Metrics
106
116
  # @param bucketDuration [String] optional interval (default no aggregation)
107
117
  # @return [Array[Hash]] datapoints
108
118
  # @see #push_data #push_data for datapoint detail
109
- def get_data(id, starts: nil, ends: nil, bucketDuration: nil)
110
- params = { start: starts, end: ends, bucketDuration: bucketDuration }
119
+ def get_data(id, starts: nil, ends: nil, bucketDuration: nil, buckets: nil)
120
+ params = { start: starts, end: ends, bucketDuration: bucketDuration, buckets: buckets }
111
121
  resp = @client.http_get("/#{@resource}/#{ERB::Util.url_encode(id)}/data/?" +
112
122
  encode_params(params))
113
123
  resp.is_a?(Array) ? resp : [] # API returns no content (empty Hash) instead of empty array
@@ -159,7 +169,7 @@ module Hawkular::Metrics
159
169
  end
160
170
  end
161
171
 
162
- # Class that interracts with "counter" metric types
172
+ # Class that interacts with "counter" metric types
163
173
  class Counters < Metrics
164
174
  # @param client [Client]
165
175
  def initialize(client)
@@ -170,19 +180,19 @@ module Hawkular::Metrics
170
180
  # @param id [String] metric definition id
171
181
  # @param starts [Integer] optional timestamp (default now - 8h)
172
182
  # @param ends [Integer] optional timestamp (default now)
173
- # @param bucketDuration [String] optional interval (default no
183
+ # @param bucket_duration [String] optional interval (default no
174
184
  # aggregation)
175
185
  # @return [Array[Hash]] rate points
176
- def get_rate(id, starts: nil, ends: nil, bucketDuration: nil)
186
+ def get_rate(id, starts: nil, ends: nil, bucket_duration: nil)
177
187
  path = "/#{@resource}/#{ERB::Util.url_encode(id)}/rate"
178
- params = { start: starts, end: ends, bucketDuration: bucketDuration }
188
+ params = { start: starts, end: ends, bucketDuration: bucket_duration }
179
189
  resp = @client.http_get(path + '?' + encode_params(params))
180
190
  # API returns no content (empty Hash) instead of empty array
181
191
  resp.is_a?(Array) ? resp : []
182
192
  end
183
193
  end
184
194
 
185
- # Class that interracts with "availability" metric types
195
+ # Class that interacts with "availability" metric types
186
196
  class Availability < Metrics
187
197
  # @param client [Client]
188
198
  def initialize(client)