circonus 1.0.1

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