atsd 1.0.1

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.
@@ -0,0 +1,19 @@
1
+ require 'faraday'
2
+ require 'faraday_middleware'
3
+ require 'hashie'
4
+
5
+ require 'atsd/version'
6
+ require 'atsd/utils'
7
+ require 'atsd/atsd'
8
+
9
+ module ATSD
10
+ # Alias for {ATSD#initialize ATSD::ATSD.new}
11
+ # @param [Hash] options
12
+ # the configuration options
13
+ # @return [ATSD]
14
+ def self.new(options = {}, &block)
15
+ ATSD.new(options, &block)
16
+ end
17
+ end
18
+
19
+
@@ -0,0 +1,54 @@
1
+ require 'atsd/client'
2
+ require 'atsd/services/series_service'
3
+ require 'atsd/services/properties_service'
4
+ require 'atsd/services/alerts_service'
5
+ require 'atsd/services/metrics_service'
6
+ require 'atsd/services/entities_service'
7
+ require 'atsd/services/entity_groups_service'
8
+
9
+ module ATSD
10
+
11
+ # Main class which holds REST client and resource services.
12
+ class ATSD
13
+ # @return [Client] REST API client
14
+ attr_reader :client
15
+
16
+ # @option options [String] :url API Endpoint
17
+ # @option options [Hash{Symbol => String}, String] :basic_auth A string 'login:password'
18
+ # or hash with :login and :password keys
19
+ # @option options [Boolean, Logger] :logger `true` to use default logger, false to disable logging
20
+ # or a custom logger
21
+ # @yield [Faraday::Connection] Modify middleware in the block
22
+ # @see http://www.rubydoc.info/gems/faraday/0.9.1/Faraday/Connection:initialize for other options
23
+ def initialize(options, &block)
24
+ @client = Client.new(options, &block)
25
+ end
26
+
27
+ class << self
28
+ # Defines a new lazy-loaded service
29
+ # @param [Symbol] name the service name
30
+ # @param [Class] type the service's type
31
+ # @!macro [attach] service
32
+ # @return [$2] the $1 service
33
+ def service(name, type)
34
+ define_method(name) do
35
+ var_name = "@#{name}"
36
+ if instance_variable_defined? var_name
37
+ instance_variable_get var_name
38
+ else
39
+ obj = type.new instance_variable_get('@client')
40
+ instance_variable_set var_name, obj
41
+ obj
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ service :series, SeriesService
48
+ service :properties, PropertiesService
49
+ service :alerts, AlertsService
50
+ service :metrics, MetricsService
51
+ service :entities, EntitiesService
52
+ service :entity_groups, EntityGroupsService
53
+ end
54
+ end
@@ -0,0 +1,469 @@
1
+ require 'atsd/middleware/errors_handler'
2
+ require 'active_support/core_ext/hash/keys'
3
+
4
+ module ATSD
5
+
6
+ # HTTP(S) Client for Axibase Time-Series Database. Implements all REST methods
7
+ # of the API in a straightforward manner.
8
+ class Client
9
+
10
+ # @return [Faraday::Connection] faraday connection object
11
+ attr_reader :connection
12
+
13
+ # @return [Logger] logger to use
14
+ attr_reader :logger
15
+
16
+ # See {ATSD#initialize ATSD.new}
17
+ def initialize(options, &block)
18
+ options = options.symbolize_keys
19
+ login, password = extract_basic_auth(options)
20
+ @logger = extract_logger(options)
21
+ url = options.delete(:url)
22
+ @connection = Faraday.new url, options do |builder|
23
+ builder.headers['User-Agent'] = "ATSD Ruby Client v#{VERSION}"
24
+ builder.basic_auth login, password
25
+ builder.request :json
26
+
27
+ builder.response :errors_handler
28
+ builder.response :json, :content_type => 'application/json'
29
+ builder.response :logger, @logger, :bodies => true if @logger
30
+
31
+ builder.adapter Faraday.default_adapter
32
+ end
33
+
34
+ if block_given?
35
+ block.arity > 0 ? yield(@connection.builder) : @connection.builder.instance_eval(&block)
36
+ end
37
+ true
38
+ end
39
+
40
+ # Query time series
41
+ #
42
+ # @param [Hash, Array<Hash>] queries query or array of queries
43
+ # @return [Array<Hash>] time series
44
+ # @raise [APIError]
45
+ def series_query(queries)
46
+ response = @connection.post 'series', :queries => Utils.ensure_array(queries)
47
+ response.body['series']
48
+ end
49
+
50
+ # Insert time series
51
+ #
52
+ # @param [Hash, Array<Hash>] series series or array of series
53
+ # @return true
54
+ # @raise [APIError]
55
+ def series_insert(series)
56
+ @connection.post 'series/insert', Utils.ensure_array(series)
57
+ true
58
+ end
59
+
60
+ # Series CSV: Insert
61
+ #
62
+ # @param [String] entity
63
+ # @param [String] data Payload - CSV containing time column and one or multiple metric columns.
64
+ # - Separator must be comma.
65
+ # - Time must be specified in Unix milliseconds.
66
+ # - Time column must be first, name of the time column could be arbitrary.
67
+ # @param [Hash] tags tag=value hash
68
+ # @return [true]
69
+ # @raise [APIError]
70
+ def series_csv_insert(entity, data, tags = {})
71
+ request = @connection.build_request(:post) do |req|
72
+ req.url("series/csv/#{entity}", tags)
73
+ req.headers["Content-Type"] = 'text/csv'
74
+ req.body = data
75
+ end
76
+
77
+ @connection.builder.build_response(@connection, request)
78
+ true
79
+ end
80
+
81
+ # Query properties
82
+ #
83
+ # @param [Hash, Array<Hash>] queries query or array of queries
84
+ # @return [Array<Hash>] array of properties
85
+ # @raise [APIError]
86
+ def properties_query(queries = nil)
87
+ response = @connection.post 'properties', :queries => Utils.ensure_array(queries)
88
+ response.body
89
+ end
90
+
91
+ # Returns properties for entity and type.
92
+ #
93
+ # @param [String] entity
94
+ # @param [String] type
95
+ # @return [Array<Hash>] array of properties
96
+ # @raise [APIError]
97
+ def properties_for_entity_and_type(entity, type)
98
+ response = @connection.get "properties/#{entity}/types#{type}"
99
+ response.body
100
+ end
101
+
102
+ # Insert properties
103
+ #
104
+ # @param [Hash, Array<Hash>] properties property or array of properties
105
+ # @return true
106
+ # @raise [APIError]
107
+ def properties_insert(properties)
108
+ @connection.post 'properties/insert', Utils.ensure_array(properties)
109
+ true
110
+ end
111
+
112
+ # Insert keys and delete keys by id or by partial key match in one request.
113
+ #
114
+ # @param [Hash, Array<Hash>] insert properties to insert
115
+ # @param [Hash, Array<Hash>] delete delete an array of properties for entity,
116
+ # type, and optionally for specified keys
117
+ # @param [Hash, Array<Hash>] delete_matchers delete rows that partially match the specified key.
118
+ # 'createdBeforeTime’ specifies an optional time condition. The server should delete all keys
119
+ # that have been created before the specified time. 'createdBeforeTime’ is specified in unix
120
+ # milliseconds.
121
+ # @return true
122
+ # @raise [APIError]
123
+ def properties_batch(insert, delete, delete_matchers)
124
+ @connection.patch 'properties', [
125
+ { :action => 'insert', :properties => Utils.ensure_array(insert) },
126
+ { :action => 'delete', :properties => Utils.ensure_array(delete) },
127
+ { :action => 'delete-match', :matchers => Utils.ensure_array(delete_matchers) }
128
+ ].select { |action| (action[:properties] || action[:matchers]).count > 0 }
129
+ true
130
+ end
131
+
132
+ # Delete an array of properties for entity, type, and optionally for specified keys
133
+ #
134
+ # @param [Hash, Array<Hash>] properties
135
+ # @return true
136
+ # @raise [APIError]
137
+ def properties_delete(properties)
138
+ properties_batch [], properties, []
139
+ end
140
+
141
+ # Delete rows that partially match the specified key
142
+ #
143
+ # @param [Hash, Array<Hash>] matchers
144
+ # @return true
145
+ # @raise [APIError]
146
+ def properties_delete_match(matchers)
147
+ properties_batch [], [], matchers
148
+ end
149
+
150
+ # Query alerts
151
+ #
152
+ # @param [Hash, Array<Hash>] queries query or array of queries
153
+ # @return [Array<Hash>] alerts
154
+ # @raise [APIError]
155
+ def alerts_query(queries = nil)
156
+ response = @connection.post 'alerts', :queries => Utils.ensure_array(queries)
157
+ response.body
158
+ end
159
+
160
+ # (De-)acknowledge and delete alerts
161
+ #
162
+ # @param [Hash, Array<Hash>] actions action or array of actions
163
+ # @return [true]
164
+ # @raise [APIError]
165
+ def alerts_update(actions)
166
+ @connection.patch 'alerts', Utils.ensure_array(actions)
167
+ true
168
+ end
169
+
170
+ # Alerts history query
171
+ #
172
+ # @param [Hash, Array<Hash>] queries query or array of queries
173
+ # @return [Array<Hash>] history records
174
+ # @raise [APIError]
175
+ def alerts_history_query(queries = nil)
176
+ response = @connection.post 'alerts/history', :queries => Utils.ensure_array(queries)
177
+ response.body
178
+ end
179
+
180
+ # Metrics list.
181
+ #
182
+ # @param [Hash] parameters
183
+ # @return [Array<Hash>]
184
+ # @raise [APIError]
185
+ def metrics_list(parameters = {})
186
+ response = @connection.get 'metrics', parameters
187
+ response.body
188
+ end
189
+
190
+ # Displays metric properties and its tags.
191
+ #
192
+ # @param [String] metric
193
+ # @return [Hash]
194
+ # @raise [APIError]
195
+ def metrics_get(metric)
196
+ response = @connection.get "metrics/#{metric}"
197
+ response.body
198
+ end
199
+
200
+ # Create a metric with specified properties and tags or replace an existing metric.
201
+ # This method creates a new metric or replaces an existing metric.
202
+ #
203
+ # @note If only a subset of fields is provided for an existing metric,
204
+ # the remaining properties and tags will be deleted.
205
+ #
206
+ # @param [String] metric
207
+ # @param [Hash] body
208
+ # @return [true]
209
+ # @raise [APIError]
210
+ def metrics_create_or_replace(metric, body)
211
+ @connection.put "metrics/#{metric}", body
212
+ true
213
+ end
214
+
215
+ # Update specified properties and tags for the given metric.
216
+ # This method updates specified properties and tags for an existing metric.
217
+ #
218
+ # @note Properties and tags that are not specified are left unchanged.
219
+ #
220
+ # @param [String] metric
221
+ # @param [Hash] body
222
+ # @return [true]
223
+ # @raise [APIError]
224
+ def metrics_update(metric, body)
225
+ @connection.patch "metrics/#{metric}", body
226
+ true
227
+ end
228
+
229
+ # Delete the metric. Data collected for the metric will be removed
230
+ # asynchronously in the background.
231
+ #
232
+ # @param [String] metric
233
+ # @return [true]
234
+ # @raise [APIError]
235
+ def metrics_delete(metric)
236
+ @connection.delete "metrics/#{metric}"
237
+ true
238
+ end
239
+
240
+ # Returns a list of unique series tags for the metric.
241
+ # The list is based on data stored on disk for the last 24 hours.
242
+ #
243
+ # @param [String] metric
244
+ # @param [Hash] parameters
245
+ # @return [Array]
246
+ # @raise [APIError]
247
+ def metrics_entity_and_tags(metric, parameters = {})
248
+ response = @connection.get "metrics/#{metric}/entity-and-tags", parameters
249
+ response.body
250
+ end
251
+
252
+ # List of entities
253
+ #
254
+ # @param [Hash] parameters
255
+ # @return [Array<Hash>]
256
+ # @raise [APIError]
257
+ def entities_list(parameters = {})
258
+ response = @connection.get 'entities', parameters
259
+ response.body
260
+ end
261
+
262
+ # Entity details
263
+ #
264
+ # @param [String] entity
265
+ # @return [Hash]
266
+ # @raise [APIError]
267
+ def entities_get(entity)
268
+ response = @connection.get "entities/#{entity}"
269
+ response.body
270
+ end
271
+
272
+ # Create or replace entity.
273
+ #
274
+ # @param [String] entity
275
+ # @param [Hash] body
276
+ # @return [true]
277
+ # @raise [APIError]
278
+ def entities_create_or_replace(entity, body)
279
+ @connection.put "entities/#{entity}", body
280
+ true
281
+ end
282
+
283
+ # Update entity.
284
+ #
285
+ # @param [String] entity
286
+ # @param [Hash] body
287
+ # @return [true]
288
+ # @raise [APIError]
289
+ def entities_update(entity, body)
290
+ @connection.patch "entities/#{entity}", body
291
+ true
292
+ end
293
+
294
+ # Delete entity.
295
+ #
296
+ # @param [String] entity
297
+ # @return [true]
298
+ # @raise [APIError]
299
+ def entities_delete(entity)
300
+ @connection.delete "entities/#{entity}"
301
+ true
302
+ end
303
+
304
+ # Property types for entity
305
+ #
306
+ # @param [String] entity
307
+ # @param [Hash] parameters
308
+ # @return [Array]
309
+ # @raise [APIError]
310
+ def entities_property_types(entity, parameters = {})
311
+ response = @connection.get "entities/#{entity}/property-types", parameters
312
+ response.body
313
+ end
314
+
315
+ # Metrics for entity
316
+ #
317
+ # @param [String] entity
318
+ # @param [Hash] parameters
319
+ # @return [Array]
320
+ # @raise [APIError]
321
+ def entities_metrics(entity, parameters = {})
322
+ response = @connection.get "entities/#{entity}/metrics", parameters
323
+ response.body
324
+ end
325
+
326
+ # Entity groups list.
327
+ #
328
+ # @param [Hash] parameters
329
+ # @return [Array]
330
+ # @raise [APIError]
331
+ def entity_groups_list(parameters = {})
332
+ response = @connection.get 'entity-groups', parameters
333
+ response.body
334
+ end
335
+
336
+ # Entity group info
337
+ #
338
+ # @param [String] entity_group
339
+ # @return [Hash]
340
+ # @raise [APIError]
341
+ def entity_groups_get(entity_group)
342
+ response = @connection.get "entity-groups/#{entity_group}"
343
+ response.body
344
+ end
345
+
346
+ # Create or replace entity group.
347
+ #
348
+ # @param [String] entity_group
349
+ # @param [Hash] body
350
+ # @return [true]
351
+ # @raise [APIError]
352
+ def entity_groups_create_or_replace(entity_group, body)
353
+ @connection.put "entity-groups/#{entity_group}", body
354
+ true
355
+ end
356
+
357
+ # Update entity group.
358
+ #
359
+ # @param [String] entity_group
360
+ # @param [Hash] body
361
+ # @return [true]
362
+ # @raise [APIError]
363
+ def entity_groups_update(entity_group, body)
364
+ @connection.patch "entity-groups/#{entity_group}", body
365
+ true
366
+ end
367
+
368
+ # Delete entity group.
369
+ #
370
+ # @param [String] entity_group
371
+ # @return [true]
372
+ # @raise [APIError]
373
+ def entity_groups_delete(entity_group)
374
+ @connection.delete "entity-groups/#{entity_group}"
375
+ true
376
+ end
377
+
378
+ # List entity group entities.
379
+ #
380
+ # @param [String] entity_group
381
+ # @param [Hash] parameters
382
+ # @return [Array]
383
+ # @raise [APIError]
384
+ def entity_groups_get_entities(entity_group, parameters = {})
385
+ response = @connection.get "entity-groups/#{entity_group}/entities", parameters
386
+ response.body
387
+ end
388
+
389
+ # Add entities to entity group.
390
+ #
391
+ # @param [String] entity_group
392
+ # @param [Array] entities
393
+ # @param [Hash] parameters
394
+ # @return [true]
395
+ # @raise [APIError]
396
+ def entity_groups_add_entities(entity_group, entities, parameters = {})
397
+ @connection.patch "entity-groups/#{entity_group}/entities", [
398
+ parameters.merge(:action => 'add',
399
+ :entities => entities)
400
+ ]
401
+ true
402
+ end
403
+
404
+ # Replace entities in entity group.
405
+ #
406
+ # @param [String] entity_group
407
+ # @param [Array] entities
408
+ # @param [Hash] parameters
409
+ # @return [true]
410
+ # @raise [APIError]
411
+ def entity_groups_replace_entities(entity_group, entities, parameters = {})
412
+ @connection.put "entity-groups/#{entity_group}/entities", entities
413
+ true
414
+ end
415
+
416
+ # Delete entities in entity group.
417
+ #
418
+ # @param [String] entity_group
419
+ # @param [Array] entities
420
+ # @return [true]
421
+ # @raise [APIError]
422
+ def entity_groups_delete_entities(entity_group, entities)
423
+ @connection.patch "entity-groups/#{entity_group}/entities", [
424
+ {:action => 'delete', :entities => entities}
425
+ ]
426
+ true
427
+ end
428
+
429
+ # Delete all entities in entity group.
430
+ #
431
+ # @param [String] entity_group
432
+ # @return [true]
433
+ # @raise [APIError]
434
+ def entity_groups_delete_all_entities(entity_group)
435
+ @connection.patch "entity-groups/#{entity_group}/entities", [
436
+ {:action => 'delete-all'}
437
+ ]
438
+ true
439
+ end
440
+
441
+ private
442
+
443
+ def extract_basic_auth(options)
444
+ auth = options.delete :basic_auth
445
+ case auth
446
+ when String
447
+ auth.split(':', 2)
448
+ when Hash
449
+ return auth[:login], auth[:password]
450
+ else
451
+ raise ArgumentError, 'Bad login/password specification'
452
+ end
453
+ end
454
+
455
+ def extract_logger(options)
456
+ logger = options.delete :logger
457
+ case logger
458
+ when true
459
+ require 'logger'
460
+ ::Logger.new(STDOUT)
461
+ when false
462
+ nil
463
+ else
464
+ logger
465
+ end
466
+ end
467
+ end
468
+ end
469
+