atsd 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+