tsdb_time_series 4.1.2

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,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ OTY2MTQyZTM3YWMwYzc3YWZlOWQyNDk1ZTBhNWM5MGNjNzYzNmE2Zg==
5
+ data.tar.gz: !binary |-
6
+ YmVkMjlhN2U1ODM5ODBmZGIzYzRkNDY5ZGViYjk1MWI5NGU5YWE2OQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MzE5ZDk3OWNmMzMxZWYwOGFkNTM1YzA5OTdmNTg3NDI4MTNiNWE4NmYyNjIx
10
+ ZDZhNTc5ZDZhYjZiNTY4YzE4N2Q1NWI2MDg2NjE1ZmFhZjEwMmVmY2QyNjhm
11
+ ZmEwZmExNzU4MzgwMmU0N2E1ZjI4NjVmZWRlMDAwOWMyMDRlZTg=
12
+ data.tar.gz: !binary |-
13
+ ODNmOTdlODViODczNGQ1YzY1ZWEwMGM1NzQyNDExZjM1ODBhMGQ5OWNmNTQ2
14
+ NDRjNWVhYmUzZDUwZTAxMTg4NjhlMGQzZjY3MDQyNzAzN2YzZWY4Mzk5NTlj
15
+ NGQxZjBhMzQ0MjMzOWU5MzMxNzYxOGViMWRjOWNhMDUyMTdkYWM=
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.swp
19
+ *.swo
20
+ bin/
21
+ vendor/
22
+ .DS_Store
@@ -0,0 +1,17 @@
1
+ LineLength:
2
+ Enabled: true
3
+ Max: 128
4
+
5
+ Encoding:
6
+ Enabled: false
7
+
8
+ CaseIndentation:
9
+ Enabled: false
10
+
11
+ # Annoying warning when dealing with Unix timestamps
12
+ NumericLiterals:
13
+ Enabled: false
14
+
15
+ # MethodLength is superseded by Reek's TooManyStatements smell-finder.
16
+ MethodLength:
17
+ Enabled: false
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm use ruby-1.9.3-p545
@@ -0,0 +1,38 @@
1
+ # v4.1.2
2
+ - Renamed gem to `tsdb_time_series` for public use. (`time_series` is already taken)
3
+ - Cleaned up README.md a little bit
4
+
5
+ # v4.1.1
6
+ - Bumped Rubocop to `~> 0.28.0` and updated dependencies
7
+ - Fixed new Rubocop warnings
8
+
9
+ # v4.1.0
10
+ - Bumped RSpec to `~> 3.0`
11
+ - Updated tests to follow betterspecs.org guidelines
12
+ - Added integration tests using Docker
13
+
14
+ # v4.0.0
15
+ - Added initial support for synthetic metrics through formula calculations.
16
+ - Dropped OpenTSDB 1.1 support (includes dropping ASCII output support!)
17
+ - Dropped client-side validation support (done by OpenTSDB now)
18
+
19
+ # v3.0.0
20
+ - Updated response signatures to include status code, result count, and explicit error messages.
21
+
22
+ # v2.4.0
23
+ - Added multi-query support (persistent, pipelined HTTP requests)
24
+
25
+ # v2.3.0
26
+ - Deprecrated client-side validation for OpenTSDB 2.0 clients
27
+
28
+ # v2.2.0
29
+ - Switched to Excon from HTTParty
30
+
31
+ # v2.1.0
32
+ - Upgraded to Ruby 1.9.3
33
+
34
+ # v2.0.1
35
+ - Added proper rate support
36
+
37
+ # v2.0.0
38
+ - Initial OpenTSDB 2.0 support
@@ -0,0 +1,22 @@
1
+ ## Contributing to TimeSeries
2
+
3
+ The best way to contribute code is to fork the main repo and send a pull request on GitHub.
4
+
5
+ Bug fixes should be done in the master branch. New features or major changes should be done in a feature branch. Alternatively, you can send a plain-text patch to the [mailing list](https://groups.google.com/forum/#!forum/opentsdb-clients).
6
+
7
+ Please break down your changes into as many small commits as possible.
8
+
9
+ Please respect the coding style of the code you're changing. TimeSeries uses Rubocop and reek to adhere to the Ruby style guide.
10
+
11
+ ## Pull Request Requirements
12
+
13
+ *Before* sending a pull request, please make sure your changes meet the following criteria:
14
+
15
+ - You have run `bundle exec rake build` and your code:
16
+ - Maintains code coverage remains at 100%
17
+ - Has fully been fully documented (`yard` coverage at 100%)
18
+ - Has no `reek` warnings
19
+ - Has no `rubocop` warnings
20
+ - All RSpec tests pass
21
+
22
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in time_series.gemspec
4
+ gemspec
@@ -0,0 +1,27 @@
1
+ # vim: ft=ruby
2
+ # More info at https://github.com/guard/guard#readme
3
+ #
4
+ # More info also at https://github.com/guard/guard-rspec -- this one in
5
+ # particular details configuration options such as whether to run all tests
6
+ # after a failing test starts passing
7
+
8
+ guard :rspec, cli: '--tag ~slow' do
9
+ watch(/^spec\/.+_spec\.rb/)
10
+ watch(/^lib\/(.+)\.rb$/) do |match|
11
+ %w(unit integration acceptance).map do |kind|
12
+ "spec/#{kind}/lib/#{match[1]}_spec.rb"
13
+ end
14
+ end
15
+ watch('spec/spec_helper.rb') { 'spec' }
16
+ watch(%r{^spec/(fixtures|resources)(/|.rb)}) { 'spec' }
17
+ end
18
+
19
+ guard :rubocop, all_on_start: false do
20
+ watch('Guardfile')
21
+ watch(/.+\.rb$/)
22
+ watch(/(?:.+\/)?\.rubocop\.yml$/) { |m| File.dirname(m[0]) }
23
+ end
24
+
25
+ guard :reek do
26
+ watch(/^lib\/(.+)\.rb$/)
27
+ end
@@ -0,0 +1,232 @@
1
+ ## tsdb_time_series
2
+
3
+ `tsdb_time_series` is a Ruby Gem for OpenTSDB that provides core tools when working with an OpenTSDB data store. With `tsdb_time_series`, you can search for registered metrics, tag keys and tag values, read from and write to the OpenTSDB, and submit multiple simultaneous queries to an OpenTSDB cluster.
4
+
5
+ ### Installation
6
+
7
+ Download the Gem from a standard Gem server like rubygems.org and install it:
8
+
9
+ gem install tsdb_time_series
10
+
11
+ Alternatively, build it from source and install it:
12
+
13
+ git clone https://github.com/opower/time_series.git
14
+ cd time_series
15
+ gem build tsdb_time_series.gemspec
16
+ gem install tsdb_time_series-4.1.3.gem
17
+
18
+ ### Usage
19
+ Once you have the OpenTSDB cluster set up, we can configure the TimeSeries Gem to talk to the API. The first step would be configuring a TimeSeries client. If no host is specified, the client connects to localhost by default. The client connects to port 4242 by default.
20
+
21
+ #### Configuring a TimeSeries Client
22
+
23
+ ```ruby
24
+ client = Opower::TimeSeries::TSClient.new('opentsdb.foo.com', 4242)
25
+ client.configure({ version: '2.0', dry_run: false, validation: true })
26
+ ```
27
+
28
+ Here is a table that lists options supported by the TimeSeries client:
29
+
30
+ | Option | Type | Description| Default value |
31
+ | ------------- | ------------- | ------------- | ------------- |
32
+ | :version | Float | version of the OpentSDB cluster the client will be talking to. If you wish to use the new 2.0 endpoints, set version to 2.0 or higher. | 2.0 |
33
+ | :dry_run | Boolean | If set to true, this gem will not run any commands, only output the generated URLs or calls to OpenTSDB. | false |
34
+ | :validation | Boolean | With this flag set to true, client performs a check to validate the metric name. | false |
35
+
36
+ #### Search for a registered metric/tagk/tagv
37
+
38
+ Using a properly configured client, you can search an OpenTSDB cluster to find suggestions for a metric, tag key, or tag value. This employs the `/api/suggest` end point of the OpenTSDB API and works as a simple namespace search. It is useful when you do not know what metric labels are being written to the OpenTSDB.
39
+
40
+ ```ruby
41
+ client.suggest('proc.stat.cpu') # suggest a metric
42
+ client.suggest('proc.stat.cpu', 'tagk') # suggest a tagk
43
+ client.suggest('proc.stat.cpu', 'tagv') # suggest a tagv
44
+ ```
45
+
46
+ #### Writing to OpenTSDB
47
+
48
+ You can use a TimeSeries client to push telnet/netcat style writes to OpenTSDB. If no hostname and port are specified, this gem defaults to 127.0.0.1:4242. To insert a metric into OpenTSDB using a configured client, create a new `Metric` object first and then use the `client.write` call as shown below :
49
+
50
+ ```ruby
51
+ metric_config = {
52
+ name: 'proc.stat.cpu',
53
+ timestamp: Time.now.to_i,
54
+ value: 10,
55
+ tags: { host: 'somehost.foo.com', type: 'iowait' }
56
+ }
57
+
58
+ metric = Opower::TimeSeries::Metric.new(metric_config)
59
+ client.write(metric)
60
+ ```
61
+
62
+
63
+ #### Reading from OpenTSDB
64
+
65
+ We can use a TimeSeries client to read metric data from an OpenTSDB cluster. To read data from OpenTSDB, you would first create a query object to run against the specified client. This query object supports all the standard options in the 2.0 API. Here is an example :
66
+
67
+ ```ruby
68
+ query_config = {
69
+ format: :png,
70
+ start: '2013/01/01-01:00:00',
71
+ end: '2013/02/01-01:00:00',
72
+ m: [{ aggregator: 'sum', metric: 'proc.stat.cpu', tags: { type: 'iowait' } }],
73
+ nocache: true
74
+ }
75
+
76
+ query = Opower::TimeSeries::Query.new(query_config)
77
+ client.run_query(query)
78
+ ```
79
+
80
+ The `Query` object accepts the following parameters:
81
+
82
+
83
+ | Option | Type | Description| Default value |
84
+ | ------------- | ------------- | ------------- | ------------- |
85
+ | :format | String | Specifies the output format. supported values include : `ascii`, `json`, `png`. | 'json' |
86
+ | :start | `String` / `Integer` / `DateTime` | The query's start date/time expressed as '2013/01/01-01:00:00' (string), '5m-ago' (String, indicating data for last 5 minutes), 1232323232 (time since epoch, Integer) or as a Ruby DateTime object. This is a required field. | none |
87
+ | :end | `String` / `Integer` / `DateTime` | The query's end date. This field supports the same types as `:start` field. This field is optional | Time.now (current time) |
88
+ | :m | `Array` | Array of JSON objects with the `aggregator`, `metrics`, and `tags` as fields: | none |
89
+
90
+ Here is a sample metrics object , that goes into the :m object .
91
+ ```ruby
92
+ m: [{ aggregator: 'sum', metric: 'proc.stat.cpu', tags: { type: 'iowait', version: 2.1 } }]
93
+ ```
94
+
95
+ Other options available to the REST API can be used here as well. Here is a list of options that have been tested to work with this gem. See the [OpenTSDB documentation](http://opentsdb.net/http-api.html#/q_Parameters) for more information :
96
+
97
+ ```
98
+
99
+ # * o Rendering options.
100
+ # * wxh The dimensions of the graph.
101
+ # * yrange The range of the left Y axis.
102
+ # * y2range The range of the right Y axis.
103
+ # * ylabel Label for the left Y axis.
104
+ # * y2label Label for the right Y axis.
105
+ # * yformat Format string for the left Y axis.
106
+ # * y2formatFormat string for the right Y axis.
107
+ # * ylog Enables log scale for the left Y axis.
108
+ # * y2log Enables log scale for the right Y axis.
109
+ # * key Options for the key (legend) of the graph.
110
+ # * nokey Removes the key (legend) from the graph.
111
+ # * nocache Forces TSD to ignore cache and fetch results from HBase.
112
+
113
+ ```
114
+
115
+
116
+
117
+ #### Example Queries
118
+
119
+ ```ruby
120
+ query_config = {
121
+ format: :ascii,
122
+ start: 14535353,
123
+ end: 16786786,
124
+ m: [{ aggregator: 'sum', metric: 'proc.stat.cpu', rate: true, tags: { type: 'iowait' } }]
125
+ }
126
+
127
+ query = Opower::TimeSeries::Query.new(query_config)
128
+ client.run_query(query)
129
+ ```
130
+
131
+ ```ruby
132
+ query_config = {
133
+ format: :json,
134
+ start: '3m-ago',
135
+ m: [{ aggregator: 'max', metric: 'proc.stat.cpu', tags: { type: 'iowait' } }],
136
+ nocache: true
137
+ }
138
+
139
+ query = Opower::TimeSeries::Query.new(query_config)
140
+ client.run_query(query)
141
+ ```
142
+
143
+ #### Running Multiple Queries Simultaneously
144
+
145
+ If you need to query multiple metrics at the same time, TimeSeries provides support for that as well:
146
+
147
+ ```ruby
148
+ queries = []
149
+ 3.times do
150
+ query_config = {
151
+ format: :ascii,
152
+ start: 14535353,
153
+ end: 16786786,
154
+ m: [{ aggregator: 'sum', metric: 'proc.stat.cpu', rate: true, tags: { type: 'iowait' } }]
155
+ }
156
+
157
+ queries << Opower::TimeSeries::Query.new(query_config)
158
+ end
159
+
160
+ client.run_queries(queries)
161
+ ```
162
+
163
+ #### Running Synthetic Metric Queries
164
+
165
+ Sometimes, you might need to create a new time series using metrics data from existing time series. We call these 'Synthetic Metric Queries'. Some examples could be disk utilization (expressed as disk used/total disk available) or CPU itilization (expressed as cpu cycles used/total cpu cycles).
166
+
167
+ TimeSeries also provides the capability to create synthetic metric queries through the use of a formula and any number of queries against OpenTSDB. Here is an example that creates a formula which adds two time series ( `x + y` ) and feeds the calculation with data from OpenTSDB :
168
+
169
+ ```ruby
170
+ metric_x = [{ metric: 'sys.numa.allocation', tags: { host: 'somehost.foo.com' } }]
171
+ query_config_x = { format: :json, start: '1h-ago', m: metric_x }
172
+ @query_metric_x = Opower::TimeSeries::Query.new(query_config_x)
173
+
174
+ metric_y = [{ metric: 'sys.numa.foreign_allocs', tags: { host: 'somehost.foo.com' } }]
175
+ query_config_y = { format: :json, start: '1h-ago', m: metric_y }
176
+ @query_metric_y = Opower::TimeSeries::Query.new(query_config_y)
177
+
178
+ name = 'My Synthetic Metric Alias'
179
+ formula = 'x + y'
180
+ query_hash = { x: @query_metric_x, y: @query_metric_y }
181
+ client.run_synthetic_query(name, formula, query_hash)
182
+ ```
183
+
184
+ The above example illustrates how you pass in a hash object to the client in order to run a sythentic query. This also indicates how the key maps to the parameters in the formula, with their corresponding values consisting of a Query object. When the calculation is performed, it will only operate on matching timestamps. If there are no matching data-points, it will return nothing.
185
+
186
+ For more information about what can be done with the formula parameters, read the documentation for the [Dentaku Calculator](https://github.com/rubysolo/dentaku). This gem expects any parameter in the formula to have a matching query in the query hash.
187
+
188
+ ##### Built-in Ruby Math support
189
+
190
+ ```ruby
191
+ name = 'My Synthetic Metric Alias'
192
+ formula = 'cos(x) + y'
193
+ query_hash = { x: @query_metric_x, y: @query_metric_y }
194
+ client.run_synthetic_query(name, formula, query_hash)
195
+ ```
196
+
197
+ Formulas in time-series can use all of the basic methods provided by the Math module from Ruby.
198
+
199
+ You must wrap nested mathematical expressions in formulas or Dentaku will attempt to pass them as separate arguments into the lambda below!
200
+
201
+ For example:
202
+ Assume x = 1, y = 2
203
+ - cos(x + y) is translated into cos(1, 'add', 2) - this calls Math.cos(1, 'add', 2) - this obviously throws an error
204
+ - cos((x + y)) is translated into cos(3) - this correctly calls Math.cos(3)
205
+
206
+ This is due to the way Dentaku handles the order of precedence; unless you wrap nested arguments, it will pass them separately.
207
+
208
+ #### Testing time_series
209
+
210
+ Test cases should be added for any new code added to this project.
211
+
212
+ Run acceptance/unit tests locally:
213
+
214
+ ```
215
+ rake spec
216
+ ```
217
+
218
+ Running integration tests:
219
+ ```
220
+ docker pull opower/opentsdb
221
+ rake integration
222
+ ```
223
+
224
+ Integration tests requires you have a `Docker` installed and have ran `docker pull opower/opentsdb` before-hand.
225
+
226
+ #### Generating Documentation
227
+
228
+ To generate the documentation for this gem, run the following:
229
+
230
+ ```
231
+ yard doc
232
+ ```
@@ -0,0 +1,44 @@
1
+ # rubocop: disable LeadingCommentSpace
2
+ #! /usr/bin/env rake
3
+ # rubocop: enable LeadingCommentSpace
4
+ require 'bundler/gem_tasks'
5
+ require 'yard'
6
+ require 'rspec/core/rake_task'
7
+ require 'reek/rake/task'
8
+ require 'rubocop/rake_task'
9
+
10
+ task default: :build
11
+
12
+ # If there are test failures, you'll need to write code to address them.
13
+ # So no point in continuing to run the style tests.
14
+ desc 'Runs unit/acceptance tests'
15
+ RSpec::Core::RakeTask.new(:spec) do |task|
16
+ task.pattern = 'spec/acceptance/**/*.rb,spec/unit/**/*.rb'
17
+ end
18
+
19
+ desc 'Runs integration tests'
20
+ RSpec::Core::RakeTask.new(:integration) do |task|
21
+ task.pattern = 'spec/integration/**/*.rb'
22
+ end
23
+
24
+ desc 'Runs yard'
25
+ YARD::Rake::YardocTask.new(:yard)
26
+
27
+ desc 'smells the lib directory, which Reek defaults to anyway'
28
+ Reek::Rake::Task.new(:reek) do |task|
29
+ task.verbose = true
30
+ end
31
+
32
+ desc 'smells the spec directory, which is less important than lib'
33
+ Reek::Rake::Task.new(:reek_tests) do |task|
34
+ task.source_files = 'spec/**/*.rb'
35
+ task.verbose = true
36
+ end
37
+
38
+ desc 'runs Rubocop'
39
+ RuboCop::RakeTask.new
40
+
41
+ desc 'Runs test and code cleanliness suite: Rubocop, Reek, rspec, and yard'
42
+ task run_guards: [:spec, :yard, :reek, :reek_tests, :rubocop]
43
+
44
+ task build: :run_guards
@@ -0,0 +1,7 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'time_series/metric'
3
+ require 'time_series/query'
4
+ require 'time_series/results/result'
5
+ require 'time_series/results/synthetic_result'
6
+ require 'time_series/ts_client'
7
+ require 'time_series/version'
@@ -0,0 +1,56 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ module Opower
4
+ module TimeSeries
5
+ # Represents a metric that can be inserted into OpenTSDB instance through a [TSDBClient] object.
6
+ class Metric
7
+ attr_reader :name, :value, :timestamp, :tags
8
+
9
+ # Initializer for the Metric class.
10
+ #
11
+ # @param [Hash] config configuration hash consisting of the following values:
12
+ # @option config [String] :name The metric name (required)
13
+ # @option config [String] :value The metric value (required)
14
+ # @option config [String, Integer, Timestamp] :timestamp The timestamp in either epoch or a TimeStamp object.
15
+ # @option config [Array] :tags Array of tags to set for this metric. (tag_key => value)
16
+ #
17
+ # @return [Metric] a new Metric object
18
+ def initialize(config = {})
19
+ validate(config, [:name, :value])
20
+
21
+ @name = config[:name]
22
+ @value = config[:value]
23
+ @timestamp = config[:timestamp] || Time.now.to_i
24
+ @tags = config[:tags] || {}
25
+ end
26
+
27
+ # Converts the metric into the format required for use by `put` to insert into OpenTSDB.
28
+ #
29
+ # @return [String] put string
30
+ def to_s
31
+ result = ''
32
+ # Format the string for OpenTSDB
33
+ @tags.each { |key, value| result += "#{key}=#{value} " }
34
+ [@name, @timestamp, @value, result.rstrip].join(' ')
35
+ end
36
+
37
+ private
38
+
39
+ # Validates the metric inputs
40
+ #
41
+ # @param [Hash] config The configuration to validate.
42
+ # @param [Array] required_fields The required fields to be set inside the configuration.
43
+ def validate(config = {}, required_fields)
44
+ # Required fields check
45
+ required_fields.each do |field|
46
+ next if config.include?(field)
47
+ fail(ArgumentError, "#{field} is required to write into OpenTSDB.")
48
+ end
49
+
50
+ # Reject if user provided timestamp as not numeric
51
+ timestamp = config[:timestamp]
52
+ fail(ArgumentError, 'Timestamp must be numeric') if timestamp && !(timestamp.is_a? Fixnum)
53
+ end
54
+ end
55
+ end
56
+ end