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