circonus 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,173 @@
1
+
2
+ module CirconusUtil
3
+
4
+ def cid2int(cid)
5
+ return cid.split('/').last.to_i
6
+ end
7
+
8
+ def do_update_check_bundle(data)
9
+ search_check_bundle = @c.list_check_bundle({'display_name' => data['display_name']})
10
+ existing = false
11
+ if search_check_bundle.any? # already exists...
12
+ existing = true
13
+ r = @c.update_check_bundle(search_check_bundle.first['_cid'],data)
14
+ else
15
+ r = @c.add_check_bundle(data)
16
+ end
17
+ if not r.nil? then
18
+ pp r
19
+ print "Success (#{existing ? 'updating' : 'adding'} #{data['display_name']})\n"
20
+ end
21
+ end
22
+
23
+ def do_update_graph(data)
24
+ search_graphs = @c.search_graph(data['title'],'title')
25
+ existing = false
26
+ if search_graphs.any? # already exists...
27
+ existing = true
28
+ r = @c.update_graph(search_graphs.first['_cid'],data)
29
+ else
30
+ r = @c.add_graph(data)
31
+ end
32
+ if not r.nil? then
33
+ pp r
34
+ print "Success (#{existing ? 'updating' : 'adding'} #{data['title']})\n"
35
+ end
36
+ end
37
+
38
+ def get_composite(options)
39
+ return get_composite_stub().merge(options)
40
+ end
41
+ def get_guide(options)
42
+ return get_guide_stub().merge(options)
43
+ end
44
+ def get_data(options)
45
+ return get_data_stub().merge(options)
46
+ end
47
+ def get_dp(options)
48
+ return get_dp_stub().merge(options)
49
+ end
50
+
51
+ def get_composite_stub()
52
+ return {
53
+ "name"=>"",
54
+ "axis"=>"l",
55
+ "stack"=>nil,
56
+ "legend_formula"=>"=ceil(VAL)",
57
+ "color"=>"#33aa33",
58
+ "data_formula"=>"",
59
+ "hidden"=>false
60
+ }
61
+ end
62
+
63
+ def get_guide_stub()
64
+ return {
65
+ "data_formula"=>"",
66
+ "name"=>"",
67
+ "color"=>"#3a3aea",
68
+ "hidden"=>false,
69
+ "legend_formula"=>"=ceil(VAL)"
70
+ }
71
+ #"color"=>"#ea3a92",
72
+ end
73
+
74
+ def get_data_stub()
75
+ return {
76
+ "access_keys"=>[],
77
+ "composites"=>[],
78
+ "guides"=>[],
79
+ "datapoints"=>[],
80
+ "max_left_y"=>nil,
81
+ "max_right_y"=>nil,
82
+ "min_left_y"=>nil,
83
+ "min_right_y"=>nil,
84
+ "style"=>"area",
85
+ "title"=>nil,
86
+ }
87
+ end
88
+
89
+ def get_dp_stub()
90
+ return {
91
+ "axis"=>"l",
92
+ "stack"=>nil,
93
+ "metric_type"=>"numeric",
94
+ "data_formula"=>nil,
95
+ "name"=>nil,
96
+ "derive"=>"counter",
97
+ "metric_name"=>nil,
98
+ "color"=>nil,
99
+ "check_id"=>nil,
100
+ "legend_formula"=>nil,
101
+ "hidden"=>false
102
+ }
103
+ end
104
+
105
+ # Generate hues of a particular variety at random (or just generate a random color altogether)
106
+ def get_rand_rgb(hue='')
107
+ r = rand
108
+ g = rand
109
+ b = rand
110
+ case hue
111
+ when 'red'
112
+ r = (r * 125) + 130
113
+ g = (g * 100) + 100
114
+ b = g
115
+ when 'orange'
116
+ r = (r * 55) + 200
117
+ g = (g * 50) + 150
118
+ b = (b * 50) + 100
119
+ when 'yellow'
120
+ r = (r * 55) + 200
121
+ g = r
122
+ b = (b * 150)
123
+ when 'green'
124
+ r = (r * 150)
125
+ g = (g * 125) + 120
126
+ b = r
127
+ when 'blue'
128
+ r = (r * 150)
129
+ g = r
130
+ b = (b * 125) + 120
131
+ when 'purple'
132
+ r = (g * 55) + 200
133
+ g = (b * 150)
134
+ b = r
135
+ else
136
+ r = r * 256
137
+ g = g * 256
138
+ b = b * 256
139
+ end
140
+ return sprintf("#%02x%02x%02x",r,g,b)
141
+ end
142
+
143
+ # No SUM(*) is available, so we have to generate a formula ourselves:
144
+ # Generate a total formula =A+B+C...... using the number of datapoints
145
+ def get_total_formula(npoints)
146
+ i = 0
147
+ formula = "="
148
+ a = 'A'..'ZZZZ'
149
+ a.each do |x|
150
+ i += 1
151
+ formula += x
152
+ break if i >= npoints
153
+ formula += "+"
154
+ end
155
+ return formula
156
+ end
157
+ def get_average_formula(npoints, step=1, offset=0)
158
+ npoints = npoints / step
159
+ i = 0
160
+ formula = "=("
161
+ a = 'A'..'ZZZZ'
162
+ a.each_with_index do |x, index|
163
+ next unless (index - offset) % step == 0
164
+ i += 1
165
+ formula += x
166
+ break if i >= npoints
167
+ formula += "+"
168
+ end
169
+ formula += ")/#{npoints}"
170
+ return formula
171
+ end
172
+
173
+ end
@@ -0,0 +1,196 @@
1
+ #!/usr/bin/env ruby
2
+ # Tue Sep 18 20:18:09 EDT 2012
3
+ # -- David Nicklay
4
+ #https://circonus.com/resources/api/templates
5
+ # --TODO caught in between v1 and v2 docs at the moment ... class is a mess because of it
6
+
7
+ require 'rubygems'
8
+ require 'restclient'
9
+ require 'cgi'
10
+ require 'pp'
11
+ require 'yajl'
12
+
13
+ class Circonus
14
+ attr_accessor :agent
15
+ attr_accessor :raise_errors
16
+ attr_accessor :debug
17
+
18
+ class Timeout < RestClient::RequestTimeout
19
+ end
20
+
21
+ DEFAULT_OPTIONS = {
22
+ :timeout => 20,
23
+ :open_timeout => 20
24
+ }
25
+
26
+ def initialize(apitoken,appname,agent, options={})
27
+ @apitoken = apitoken
28
+ @debug = true
29
+ @raise_errors = false
30
+ @appname = appname
31
+ @agent = agent
32
+ @headers = {
33
+ "X-Circonus-Auth-Token" => @apitoken,
34
+ "X-Circonus-App-Name" => @appname,
35
+ "Accept" => 'application/json'
36
+ }
37
+ @url_v1_prefix = "https://circonus.com/api/json/"
38
+ @url_prefix = "https://api.circonus.com/v2/"
39
+ @options = DEFAULT_OPTIONS.merge(options)
40
+ end
41
+
42
+ def _rest(type,url,headers,data=nil)
43
+ begin
44
+ resource = RestClient::Resource.new url, :timeout => @options[:timeout], :open_timeout => @options[:open_timeout]
45
+ case type
46
+ when 'delete'
47
+ r = resource.delete headers
48
+ when 'post'
49
+ r = resource.post Yajl::Encoder.encode(data), headers
50
+ when 'put'
51
+ r = resource.put Yajl::Encoder.encode(data), headers
52
+ else 'get'
53
+ r = resource.get headers
54
+ end
55
+ rescue RestClient::Forbidden,RestClient::BadRequest,RestClient::InternalServerError,RestClient::MethodNotAllowed => e
56
+ err = Yajl::Parser.parse(e.response)
57
+ print "Error (#{e.http_code}): ",err['error']," [#{e.http_body}]\n" if @debug
58
+ raise if @raise_errors
59
+ return nil,err
60
+ rescue RestClient::RequestTimeout
61
+ raise Circonus::Timeout
62
+ end
63
+ return r
64
+ end
65
+
66
+ def get(method,id)
67
+ cid = id.to_s.split('/').last
68
+ url = @url_prefix + method + '/' + cid
69
+ #print "url=#{url}\n"
70
+ r,err = _rest('get',url, @headers)
71
+ return nil,err if r.nil?
72
+ return Yajl::Parser.parse(r)
73
+ end
74
+
75
+ def delete(method,id)
76
+ cid = id.to_s.split('/').last
77
+ url = @url_prefix + method + '/' + cid
78
+ r,err = _rest('delete',url, @headers)
79
+ return nil,err if r.nil?
80
+ return Yajl::Parser.parse(r)
81
+ end
82
+
83
+ def add(method,data)
84
+ r, err = _rest('post',@url_prefix + method, @headers, data)
85
+ return nil,err if r.nil?
86
+ return Yajl::Parser.parse(r)
87
+ end
88
+
89
+ def update(method,id,data)
90
+ cid = id.to_s.split('/').last
91
+ r, err = _rest('put',@url_prefix + method + '/' + cid, @headers, data)
92
+ return nil,err if r.nil?
93
+ return Yajl::Parser.parse(r)
94
+ end
95
+
96
+ def list(method,filter=nil)
97
+ url = @url_prefix + method
98
+ if (not filter.nil?) and filter.any?
99
+ query_string = filter.map { |k,v| "f_#{CGI::escape(k)}=#{CGI::escape(v)}" }.join('&')
100
+ url += '?' + query_string
101
+ end
102
+ r, err = _rest('get',url,@headers)
103
+ return nil,err if r.nil?
104
+ return Yajl::Parser.parse(r)
105
+ end
106
+
107
+ def v1_list(method)
108
+ url = @url_v1_prefix + 'list_' + method
109
+ r, err = _rest('get',url,@headers)
110
+ return nil,err if r.nil?
111
+ return Yajl::Parser.parse(r)
112
+ end
113
+
114
+ def v1_get(method,id)
115
+ cid = id.to_s.split('/').last
116
+ headers = @headers.merge({:params => {"#{method}_id".to_sym => cid}}) # attempt to guess at the id name expected
117
+ url = @url_v1_prefix + 'get_' + method
118
+ #print "url=#{url}\n"
119
+ #pp headers
120
+ r, err = _rest('get',url, headers)
121
+ return nil,err if r.nil?
122
+ return Yajl::Parser.parse(r)
123
+ end
124
+
125
+ def v1_delete(method,id)
126
+ cid = id.to_s.split('/').last
127
+ headers = @headers.merge({:params => {"#{method}_id".to_sym => cid}}) # attempt to guess at the id name expected
128
+ url = @url_v1_prefix + 'delete_' + method
129
+ r, err = _rest('delete',url, headers)
130
+ return nil,err if r.nil?
131
+ return Yajl::Parser.parse(r)
132
+ end
133
+
134
+ def v1_add(method, data, params)
135
+ headers = @headers.merge({:params => params})
136
+ r,err = _rest 'get',@url_v1_prefix + 'add_' + method, headers, data
137
+ return nil,err if r.nil?
138
+ return Yajl::Parser.parse(r)
139
+ end
140
+
141
+ # Version 2
142
+ # Not all these are available:
143
+ %w{ account annotation broker check_bundle contact_group graph rule_set template user worksheet check }.each do |m|
144
+ define_method("list_#{m}".to_sym) do |*filter|
145
+ return list(m,filter.first)
146
+ end
147
+ define_method("get_#{m}".to_sym) do |id|
148
+ return get(m,id)
149
+ end
150
+ define_method("delete_#{m}".to_sym) do |id|
151
+ return delete(m,id)
152
+ end
153
+ define_method("add_#{m}".to_sym) do |data|
154
+ return add(m,data)
155
+ end
156
+ define_method("update_#{m}".to_sym) do |id,data|
157
+ return update(m,id,data)
158
+ end
159
+ define_method ("search_#{m}".to_sym) do |match,field|
160
+ return list(m).select { |t| t[field].match(match) }
161
+ end
162
+ end
163
+
164
+ # extraction of time ranged data (this one is a bit different from the other v2 ones)
165
+ def get_data(cid,metric,params = {})
166
+ params['start'] = (Time.now - 3600).to_i unless params.has_key? 'start'
167
+ params['end'] = Time.now.to_i unless params.has_key? 'end'
168
+ params['period'] = 300 unless params.has_key? 'period'
169
+ params['type'] = 'numeric' unless params.has_key? 'type'
170
+ url = @url_prefix + 'data' + '/' + cid + '_' + metric
171
+ #puts "url=#{url}" if @debug
172
+ headers = @headers.merge({:params => params})
173
+ r,err = _rest('get',url, headers)
174
+ return nil,err if r.nil?
175
+ return Yajl::Parser.parse(r)
176
+ end
177
+
178
+ # Version 1 (Deprecated)
179
+ %w{ metric check template annotation graph worksheet rule account user agent }.each do |m|
180
+ define_method("v1_list_#{m}s".to_sym) do
181
+ return v1_list(m + 's')
182
+ end
183
+ define_method("v1_get_#{m}".to_sym) do |id|
184
+ return v1_get(m,id)
185
+ end
186
+ define_method("v1_delete_#{m}".to_sym) do |id|
187
+ return v1_delete(m,id)
188
+ end
189
+ define_method("v1_add_#{m}".to_sym) do |data,params|
190
+ return v1_add(m,data,params)
191
+ end
192
+ define_method ("v1_search_#{m}".to_sym) do |match,field|
193
+ return v1_list(m + 's').select { |t| t[field].match(match) }
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env ruby
2
+ # Query Circonus data (used by the Circonus UI's javascript)
3
+ # Notes:
4
+ # - This is not the permanent API for this....
5
+ #
6
+ # Tue Oct 9 11:11:01 EDT 2012
7
+ # -- David Nicklay
8
+
9
+ require 'rubygems'
10
+ require 'net/http' # necessarily evil ... rest-client won't return set-cookie values on 302s....
11
+ require 'net/https'
12
+ require 'pp'
13
+ require 'cgi'
14
+ require 'yajl'
15
+
16
+
17
+ class Circonus
18
+ class Values
19
+ attr_accessor :raise_errors
20
+ attr_accessor :debug
21
+
22
+ def initialize(username,password,account='')
23
+ @username = username
24
+ @password = password
25
+ @cookie = ''
26
+ @debug = true
27
+ @host = 'circonus.com'
28
+ @login_host = "login.circonus.com"
29
+ @account = account
30
+ @data_host = "#{@account}.circonus.com"
31
+ @raise_errors = false
32
+ @data_prefix = "/json/graph/data/"
33
+ @metrics_prefix = "/account/#{@account}/json/metrics/value"
34
+
35
+ @headers = {
36
+ "X-Circonus-Auth-Token" => @apitoken,
37
+ "X-Circonus-App-Name" => @appname,
38
+ "Accept" => 'application/json'
39
+ }
40
+ @url_v1_prefix = "https://circonus.com/api/json/"
41
+ @url_prefix = "https://api.circonus.com/v2/"
42
+ end
43
+
44
+ # You need to call login before doing anything ... This gives us our session id
45
+ def login()
46
+ http = Net::HTTP.new(@login_host, 443)
47
+ http.use_ssl = true
48
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
49
+ path = '/login'
50
+ headers = {
51
+ 'Content-Type' => 'application/x-www-form-urlencoded'
52
+ }
53
+ data="login_username=#{CGI.escape @username}&login_password=#{CGI.escape @password}&login_remember=1&whereTo=https://#{@account}.circonus.com%2Flogin&&login_submit=Sign+In+%BB&welcome_submit=Sign+In+%BB"
54
+
55
+ resp = http.post(path, data, headers)
56
+ @cookie = resp.response['set-cookie'].split('; ')[0]
57
+ return true
58
+ end
59
+
60
+ # Get the value of a particular metric name
61
+ def metric_value(checkid,metric_name)
62
+ http = Net::HTTP.new(@data_host, 443)
63
+ http.use_ssl = true
64
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
65
+ path = "#{@metrics_prefix}?check_id=#{CGI.escape checkid.to_s}&metric_name=#{CGI.escape metric_name}"
66
+ headers = {
67
+ 'Cookie' => @cookie,
68
+ 'Accept' => 'application/application/json, text/javascript, */*; q=0.01'
69
+ }
70
+ resp = http.get(path, headers)
71
+ return Yajl::Parser.parse(resp.body)
72
+ end
73
+
74
+ # Get the range of data values from start to end time (t_start and t_end should be Time class vars)
75
+ def graph_data(uuid,t_start=nil,t_end=nil)
76
+ http = Net::HTTP.new(@data_host, 443)
77
+ http.use_ssl = true
78
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
79
+ # set sane defaults
80
+ t_end = Time.new if t_end.nil?
81
+ t_start = (t_end - 300) if t_start.nil?
82
+ # convert to strings
83
+ s_t_mid = (t_start + ((t_end - t_start) / 2)).strftime('%s')
84
+ s_t_start = t_start.strftime('%s')
85
+ s_t_end = t_end.strftime('%s')
86
+ uuid = uuid.split('/').last # make sure we don't have /graph in the id....
87
+ path = "#{@data_prefix}#{uuid}?start=#{s_t_start}000&end=#{s_t_end}000cnt=&type=&times=epoch_ms&_=#{s_t_mid}000"
88
+ headers = {
89
+ 'Cookie' => @cookie,
90
+ 'Accept' => 'application/application/json, text/javascript, */*; q=0.01'
91
+ }
92
+ resp = http.get(path, headers)
93
+ return Yajl::Parser.parse(resp.body)
94
+ end
95
+
96
+ # Convenience function ... get the last graph data points
97
+ # (We use 300 seconds to make sure we at least have something....)
98
+ def last_graph_data(uuid)
99
+ t = Time.new
100
+ return graph_data(uuid,t - 300, t)
101
+ end
102
+
103
+ # Find the first valid data point in our set
104
+ def _first_valid_datapoint(data)
105
+ return nil if data.nil? or not data.any?
106
+ begin
107
+ data.reverse.select { |s| not s[1].nil? }.first[1]
108
+ rescue Exception => e
109
+ return nil
110
+ end
111
+ end
112
+
113
+ # Get the sum of the last valid graph point
114
+ def total_last_graph_data(uuid)
115
+ data = last_graph_data(uuid)
116
+ sum = 0
117
+ data['data'].each do |d|
118
+ next unless d['metric_type'] == 'numeric'
119
+ sum += _first_valid_datapoint(d['data']).to_f
120
+ end
121
+ return sum
122
+ end
123
+
124
+ def eval_formula(formula,values=[])
125
+ formula = formula.clone
126
+ formula.tr!('^A-Za-z0-9/+.*_)(-','' ) # prevent injection
127
+ formula.tr!('A-Z','a-z')
128
+ formula.gsub!(/[a-z]+/) { |n| "var_#{n}" } # prevent clobbering of ruby keywords
129
+ evalstr = ""
130
+ ('a'..'zzzz').each_with_index do |x,i|
131
+ break if i == values.length
132
+ evalstr += "var_#{x}=#{values[i].to_f.to_s}\n" # force an s->i->s conversion to prevent injection in the values
133
+ end
134
+ results = eval "#{evalstr}\n#{formula}\n"
135
+ end
136
+
137
+ def eval_composite(composite,values)
138
+ return eval_formula(composite['reconnoiter_source_expression'],values)
139
+ end
140
+
141
+ def eval_composites(uuid)
142
+ data = last_graph_data(uuid)
143
+ values = []
144
+ composites = []
145
+ data['data'].each do |d|
146
+ if d['metric_type'] == 'numeric'
147
+ values << _first_valid_datapoint(d['data']).to_f
148
+ elsif d['metric_type'] == 'composite'
149
+ composites << d
150
+ end
151
+ end
152
+ composites.each do |composite|
153
+ composite['result'] = eval_composite(composite,values)
154
+ end
155
+ return composites
156
+ end
157
+
158
+ end
159
+ end
160
+