atsd 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +11 -0
- data/.rspec +2 -0
- data/Gemfile +8 -0
- data/README.md +365 -0
- data/Rakefile +16 -0
- data/atsd.gemspec +36 -0
- data/lib/atsd.rb +19 -0
- data/lib/atsd/atsd.rb +54 -0
- data/lib/atsd/client.rb +469 -0
- data/lib/atsd/errors/api_error.rb +11 -0
- data/lib/atsd/errors/error.rb +3 -0
- data/lib/atsd/middleware/errors_handler.rb +88 -0
- data/lib/atsd/models/alert.rb +7 -0
- data/lib/atsd/models/alert_history.rb +7 -0
- data/lib/atsd/models/base_model.rb +23 -0
- data/lib/atsd/models/entity.rb +7 -0
- data/lib/atsd/models/entity_group.rb +8 -0
- data/lib/atsd/models/metric.rb +7 -0
- data/lib/atsd/models/property.rb +11 -0
- data/lib/atsd/models/series.rb +8 -0
- data/lib/atsd/queries/alerts_history_query.rb +31 -0
- data/lib/atsd/queries/alerts_query.rb +31 -0
- data/lib/atsd/queries/base_query.rb +40 -0
- data/lib/atsd/queries/properties_query.rb +31 -0
- data/lib/atsd/queries/series_query.rb +150 -0
- data/lib/atsd/services/alerts_service.rb +88 -0
- data/lib/atsd/services/base_service.rb +15 -0
- data/lib/atsd/services/entities_service.rb +130 -0
- data/lib/atsd/services/entity_groups_service.rb +186 -0
- data/lib/atsd/services/metrics_service.rb +122 -0
- data/lib/atsd/services/properties_service.rb +63 -0
- data/lib/atsd/services/series_service.rb +53 -0
- data/lib/atsd/utils.rb +20 -0
- data/lib/atsd/utils/camelize_keys.rb +41 -0
- data/lib/atsd/utils/underscore_keys.rb +21 -0
- data/lib/atsd/version.rb +3 -0
- metadata +264 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'atsd/errors/api_error'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Faraday Middleware
|
5
|
+
module Middleware
|
6
|
+
# Middleware to raise error on request fail and possibly catch error message from API.
|
7
|
+
class ErrorsHandlerMiddleware < Faraday::Response::Middleware
|
8
|
+
# HTTP status codes with messages generated by
|
9
|
+
#
|
10
|
+
# ```bash
|
11
|
+
# curl -s www.iana.org/assignments/http-status-codes/http-status-codes-1.csv | \
|
12
|
+
# ruby -ne 'm = /^(\d{3}),(?!Unassigned|\(Unused\))([^,]+)/.match($_) and \
|
13
|
+
# puts "#{m[1]} => \x27#{m[2].strip}\x27,"'
|
14
|
+
# ```
|
15
|
+
HTTP_STATUS_CODES = {
|
16
|
+
100 => 'Continue',
|
17
|
+
101 => 'Switching Protocols',
|
18
|
+
102 => 'Processing',
|
19
|
+
200 => 'OK',
|
20
|
+
201 => 'Created',
|
21
|
+
202 => 'Accepted',
|
22
|
+
203 => 'Non-Authoritative Information',
|
23
|
+
204 => 'No Content',
|
24
|
+
205 => 'Reset Content',
|
25
|
+
206 => 'Partial Content',
|
26
|
+
207 => 'Multi-Status',
|
27
|
+
208 => 'Already Reported',
|
28
|
+
226 => 'IM Used',
|
29
|
+
300 => 'Multiple Choices',
|
30
|
+
301 => 'Moved Permanently',
|
31
|
+
302 => 'Found',
|
32
|
+
303 => 'See Other',
|
33
|
+
304 => 'Not Modified',
|
34
|
+
305 => 'Use Proxy',
|
35
|
+
307 => 'Temporary Redirect',
|
36
|
+
308 => 'Permanent Redirect',
|
37
|
+
400 => 'Bad Request',
|
38
|
+
401 => 'Unauthorized',
|
39
|
+
402 => 'Payment Required',
|
40
|
+
403 => 'Forbidden',
|
41
|
+
404 => 'Not Found',
|
42
|
+
405 => 'Method Not Allowed',
|
43
|
+
406 => 'Not Acceptable',
|
44
|
+
407 => 'Proxy Authentication Required',
|
45
|
+
408 => 'Request Timeout',
|
46
|
+
409 => 'Conflict',
|
47
|
+
410 => 'Gone',
|
48
|
+
411 => 'Length Required',
|
49
|
+
412 => 'Precondition Failed',
|
50
|
+
413 => 'Payload Too Large',
|
51
|
+
414 => 'URI Too Long',
|
52
|
+
415 => 'Unsupported Media Type',
|
53
|
+
416 => 'Range Not Satisfiable',
|
54
|
+
417 => 'Expectation Failed',
|
55
|
+
421 => 'Misdirected Request',
|
56
|
+
422 => 'Unprocessable Entity',
|
57
|
+
423 => 'Locked',
|
58
|
+
424 => 'Failed Dependency',
|
59
|
+
426 => 'Upgrade Required',
|
60
|
+
428 => 'Precondition Required',
|
61
|
+
429 => 'Too Many Requests',
|
62
|
+
431 => 'Request Header Fields Too Large',
|
63
|
+
500 => 'Internal Server Error',
|
64
|
+
501 => 'Not Implemented',
|
65
|
+
502 => 'Bad Gateway',
|
66
|
+
503 => 'Service Unavailable',
|
67
|
+
504 => 'Gateway Timeout',
|
68
|
+
505 => 'HTTP Version Not Supported',
|
69
|
+
506 => 'Variant Also Negotiates',
|
70
|
+
507 => 'Insufficient Storage',
|
71
|
+
508 => 'Loop Detected',
|
72
|
+
510 => 'Not Extended',
|
73
|
+
511 => 'Network Authentication Required',
|
74
|
+
}
|
75
|
+
|
76
|
+
def call(environment)
|
77
|
+
@app.call(environment).on_complete do |env|
|
78
|
+
unless env.success?
|
79
|
+
message = env.body['error'] || HTTP_STATUS_CODES[env.status]
|
80
|
+
fail APIError.new(env), message
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
Faraday::Response.register_middleware :errors_handler => ATSD::Middleware::ErrorsHandlerMiddleware
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ATSD
|
2
|
+
# Base class for all models
|
3
|
+
# @abstract
|
4
|
+
class BaseModel < ::Hash
|
5
|
+
include ::Hashie::Extensions::MethodAccess
|
6
|
+
include ::Hashie::Extensions::MergeInitializer
|
7
|
+
include Utils::UnderscoreKeys
|
8
|
+
|
9
|
+
# Converts model to hash usable for API
|
10
|
+
#
|
11
|
+
# @return [Hash]
|
12
|
+
def to_request_hash
|
13
|
+
hash = {}
|
14
|
+
keys.each do |k|
|
15
|
+
new_key = k.to_s.camelize(:lower)
|
16
|
+
new_key = new_key.to_sym if k.is_a? Symbol
|
17
|
+
hash[new_key] = self[k]
|
18
|
+
end
|
19
|
+
hash
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'atsd/models/base_model'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Property model
|
5
|
+
#
|
6
|
+
# @note Please use `property[:key]` instead of `property.key` to access
|
7
|
+
# `key` attribute due to implementation details (`#key` is a Hash method).
|
8
|
+
class Property < BaseModel
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'atsd/queries/base_query'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Class for building and executing Alerts History Query
|
5
|
+
# @see https://axibase.com/atsd/api/#alerts:-history-query
|
6
|
+
class AlertsHistoryQuery < BaseQuery
|
7
|
+
TO_MILLISECONDS_LAMBDA = ->(v) do
|
8
|
+
case v
|
9
|
+
when Time
|
10
|
+
v.to_i * 1_000
|
11
|
+
else
|
12
|
+
v.to_i
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
coerce_key :end_time, TO_MILLISECONDS_LAMBDA
|
17
|
+
coerce_key :start_time, TO_MILLISECONDS_LAMBDA
|
18
|
+
|
19
|
+
# @return [Array<AlertHistory>]
|
20
|
+
def result
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return (see #result)
|
25
|
+
def execute
|
26
|
+
result = @client.alerts_history_query to_request_hash
|
27
|
+
@result = result.map { |json| AlertHistory.new json }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'atsd/queries/base_query'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Class for building and executing Alerts Query
|
5
|
+
# @see https://axibase.com/atsd/api/#alerts:-query
|
6
|
+
class AlertsQuery < BaseQuery
|
7
|
+
# `severity` levels
|
8
|
+
module Severity
|
9
|
+
UNDEFINED = 0
|
10
|
+
UNKNOWN = 1
|
11
|
+
NORMAL = 2
|
12
|
+
WARNING = 3
|
13
|
+
MINOR = 4
|
14
|
+
MAJOR = 5
|
15
|
+
CRITICAL = 6
|
16
|
+
FATAL = 7
|
17
|
+
end
|
18
|
+
|
19
|
+
# @return [Array<Alert>]
|
20
|
+
def result
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return (see #result)
|
25
|
+
def execute
|
26
|
+
result = @client.alerts_query to_request_hash
|
27
|
+
@result = result.map { |json| Alert.new json }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module ATSD
|
2
|
+
# Base class for API query builders
|
3
|
+
# @abstract
|
4
|
+
class BaseQuery < ::Hashie::Clash
|
5
|
+
include ::Hashie::Extensions::Coercion
|
6
|
+
|
7
|
+
# @return [Client]
|
8
|
+
attr_reader :client
|
9
|
+
|
10
|
+
# @param [Client] client
|
11
|
+
def initialize(client)
|
12
|
+
@client = client
|
13
|
+
end
|
14
|
+
|
15
|
+
# Build request parameters hash
|
16
|
+
# @return [Hash]
|
17
|
+
def to_request_hash
|
18
|
+
Utils::CamelizeKeys.camelize_keys(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Execute query on client
|
22
|
+
# @return (see #result)
|
23
|
+
# @raise [APIError]
|
24
|
+
def execute
|
25
|
+
raise NotImplementedError
|
26
|
+
end
|
27
|
+
|
28
|
+
# Result of query execution.
|
29
|
+
#
|
30
|
+
# @return [Object]
|
31
|
+
# @raise [APIError]
|
32
|
+
def result
|
33
|
+
@result ||= execute
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
attr_writer :result
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'atsd/queries/base_query'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Class for building and executing Properties Query
|
5
|
+
# @see https://axibase.com/atsd/api/#properties:-query
|
6
|
+
class PropertiesQuery < BaseQuery
|
7
|
+
TO_MILLISECONDS_LAMBDA = ->(v) do
|
8
|
+
case v
|
9
|
+
when Time
|
10
|
+
v.to_i * 1_000
|
11
|
+
else
|
12
|
+
v.to_i
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
coerce_key :end_time, TO_MILLISECONDS_LAMBDA
|
17
|
+
coerce_key :start_time, TO_MILLISECONDS_LAMBDA
|
18
|
+
|
19
|
+
# @return [Array<Property>]
|
20
|
+
def result
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return (see #result)
|
25
|
+
def execute
|
26
|
+
result = @client.properties_query to_request_hash
|
27
|
+
@result = result.map { |json| Property.new json }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require 'atsd/queries/base_query'
|
2
|
+
|
3
|
+
module ATSD
|
4
|
+
# Class for building and executing Series Query
|
5
|
+
# @see https://axibase.com/atsd/api/#series:-query
|
6
|
+
class SeriesQuery < BaseQuery
|
7
|
+
|
8
|
+
# @!method type(type)
|
9
|
+
# specifies source for underlying data
|
10
|
+
# @param [String] type see {Type} for possible values
|
11
|
+
# @return [self]
|
12
|
+
|
13
|
+
# `type` parameter possible values
|
14
|
+
module Type
|
15
|
+
HISTORY = 'HISTORY' # default
|
16
|
+
FORECAST = 'FORECAST'
|
17
|
+
FORECAST_DEVIATION = 'FORECAST_DEVIATION'
|
18
|
+
end
|
19
|
+
|
20
|
+
# `join.type` parameter possible values
|
21
|
+
module JoinType
|
22
|
+
COUNT = 'COUNT'
|
23
|
+
MIN = 'MIN'
|
24
|
+
MAX = 'MAX'
|
25
|
+
AVG = 'AVG'
|
26
|
+
SUM = 'SUM'
|
27
|
+
PERCENTILE_999 = 'PERCENTILE_999'
|
28
|
+
PERCENTILE_995 = 'PERCENTILE_995'
|
29
|
+
PERCENTILE_99 = 'PERCENTILE_99'
|
30
|
+
PERCENTILE_95 = 'PERCENTILE_95'
|
31
|
+
PERCENTILE_90 = 'PERCENTILE_90'
|
32
|
+
PERCENTILE_75 = 'PERCENTILE_75'
|
33
|
+
PERCENTILE_50 = 'PERCENTILE_50'
|
34
|
+
STANDARD_DEVIATION = 'STANDARD_DEVIATION'
|
35
|
+
end
|
36
|
+
|
37
|
+
# `join.interpolate` parameter possible values
|
38
|
+
module JoinInterpolate
|
39
|
+
STEP = 'STEP' # default
|
40
|
+
NONE = 'NONE'
|
41
|
+
LINEAR = 'LINEAR'
|
42
|
+
end
|
43
|
+
|
44
|
+
# `aggregate.type` parameter possible values
|
45
|
+
module AggregateType
|
46
|
+
DETAIL = 'DETAIL'
|
47
|
+
COUNT = 'COUNT'
|
48
|
+
MIN = 'MIN'
|
49
|
+
MAX = 'MAX'
|
50
|
+
AVG = 'AVG'
|
51
|
+
SUM = 'SUM'
|
52
|
+
PERCENTILE_999 = 'PERCENTILE_999'
|
53
|
+
PERCENTILE_995 = 'PERCENTILE_995'
|
54
|
+
PERCENTILE_99 = 'PERCENTILE_99'
|
55
|
+
PERCENTILE_95 = 'PERCENTILE_95'
|
56
|
+
PERCENTILE_90 = 'PERCENTILE_90'
|
57
|
+
PERCENTILE_75 = 'PERCENTILE_75'
|
58
|
+
PERCENTILE_50 = 'PERCENTILE_50'
|
59
|
+
STANDARD_DEVIATION = 'STANDARD_DEVIATION'
|
60
|
+
FIRST = 'FIRST'
|
61
|
+
LAST = 'LAST'
|
62
|
+
DELTA = 'DELTA'
|
63
|
+
WAVG = 'WAVG'
|
64
|
+
WTAVG = 'WTAVG'
|
65
|
+
THRESHOLD_COUNT = 'THRESHOLD_COUNT'
|
66
|
+
THRESHOLD_DURATION = 'THRESHOLD_DURATION'
|
67
|
+
THRESHOLD_PERCENT = 'THRESHOLD_PERCENT'
|
68
|
+
end
|
69
|
+
|
70
|
+
# `aggregate.interpolate` parameter possible values
|
71
|
+
module AggregateInterpolate
|
72
|
+
STEP = 'STEP'
|
73
|
+
NONE = 'NONE'
|
74
|
+
LINEAR = 'LINEAR'
|
75
|
+
end
|
76
|
+
|
77
|
+
# interval's unit possible values
|
78
|
+
module Interval
|
79
|
+
MILLISECOND = 'MILLISECOND'
|
80
|
+
SECOND = 'SECOND'
|
81
|
+
MINUTE = 'MINUTE'
|
82
|
+
HOUR = 'HOUR'
|
83
|
+
DAY = 'DAY'
|
84
|
+
WEEK = 'WEEK'
|
85
|
+
MONTH = 'MONTH'
|
86
|
+
QUARTER = 'QUARTER'
|
87
|
+
YEAR = 'YEAR'
|
88
|
+
end
|
89
|
+
|
90
|
+
TO_MILLISECONDS_LAMBDA = ->(v) do
|
91
|
+
case v
|
92
|
+
when Time
|
93
|
+
v.to_i * 1_000
|
94
|
+
else
|
95
|
+
v.to_i
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
coerce_key :end_time, TO_MILLISECONDS_LAMBDA
|
100
|
+
coerce_key :start_time, TO_MILLISECONDS_LAMBDA
|
101
|
+
|
102
|
+
# @return [Array<Series>]
|
103
|
+
def result
|
104
|
+
super
|
105
|
+
end
|
106
|
+
|
107
|
+
# Add tag to `tags` parameter
|
108
|
+
#
|
109
|
+
# @param [String] name tag name
|
110
|
+
# @param [String, Array<String>] values possible tag value of array of values.
|
111
|
+
# `*` and `?` wildcards allowed.
|
112
|
+
# @return [self]
|
113
|
+
def tag_add(name, values)
|
114
|
+
self[:tags][name] = Utils.ensure_array(values)
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
# Executes query and sets `result` attribute.
|
119
|
+
#
|
120
|
+
# @return (see #result)
|
121
|
+
def execute
|
122
|
+
result = @client.series_query to_request_hash
|
123
|
+
@result = result.map { |series_json| Series.new series_json }
|
124
|
+
end
|
125
|
+
|
126
|
+
# Executes multiple queries at the same time. Adds `request_id` parameter
|
127
|
+
# to each query if necessary. All queries will have a corresponding `series`
|
128
|
+
# attribute set.
|
129
|
+
#
|
130
|
+
# @param [SeriesQuery] others list of series query to execute with receiver
|
131
|
+
# @return [Array<Series>] all results
|
132
|
+
def execute_with(*others)
|
133
|
+
others ||= []
|
134
|
+
queries = [self] + others
|
135
|
+
queries_by_id = {}
|
136
|
+
if queries.count > 1
|
137
|
+
queries.each_with_index do |query, index|
|
138
|
+
query.request_id("query_#{index}") unless query.request_id
|
139
|
+
query.result = []
|
140
|
+
queries_by_id[query[:request_id]] = query
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
results = @client.series_query queries.map {|q| q.to_request_hash}
|
145
|
+
results.map! { |json| Series.new(json) }
|
146
|
+
results.each { |r| queries_by_id[r.request_id].result << r }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|