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.
- data/bin/circonus-cli +31 -0
- data/bin/circonus-data-cli +25 -0
- data/examples/add_apache_node.rb +100 -0
- data/examples/add_dns_monitor.rb +115 -0
- data/examples/add_http_check.rb +88 -0
- data/examples/add_nginx_alerts.rb +132 -0
- data/examples/add_nginx_graphs.rb +131 -0
- data/examples/add_nginx_node.rb +87 -0
- data/examples/add_snmp_node.rb +142 -0
- data/examples/cache_copy.rb +21 -0
- data/examples/update_nginx_graph.rb +131 -0
- data/examples/util.rb +173 -0
- data/lib/circonus.rb +196 -0
- data/lib/circonus/values.rb +160 -0
- metadata +108 -0
data/examples/util.rb
ADDED
@@ -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
|
data/lib/circonus.rb
ADDED
@@ -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=×=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
|
+
|