rubix 0.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/LICENSE +20 -0
- data/README.rdoc +262 -0
- data/VERSION +1 -0
- data/bin/zabbix_api +60 -0
- data/bin/zabbix_pipe +77 -0
- data/lib/rubix.rb +42 -0
- data/lib/rubix/connection.rb +111 -0
- data/lib/rubix/examples/es_monitor.rb +130 -0
- data/lib/rubix/examples/hbase_monitor.rb +87 -0
- data/lib/rubix/examples/mongo_monitor.rb +125 -0
- data/lib/rubix/log.rb +70 -0
- data/lib/rubix/model.rb +56 -0
- data/lib/rubix/models/application.rb +76 -0
- data/lib/rubix/models/host.rb +127 -0
- data/lib/rubix/models/host_group.rb +74 -0
- data/lib/rubix/models/item.rb +122 -0
- data/lib/rubix/models/template.rb +81 -0
- data/lib/rubix/monitor.rb +167 -0
- data/lib/rubix/monitors/chef_monitor.rb +82 -0
- data/lib/rubix/monitors/cluster_monitor.rb +84 -0
- data/lib/rubix/response.rb +124 -0
- data/lib/rubix/sender.rb +301 -0
- data/spec/rubix/connection_spec.rb +43 -0
- data/spec/rubix/models/host_group_spec.rb +56 -0
- data/spec/rubix/monitor_spec.rb +81 -0
- data/spec/rubix/monitors/chef_monitor_spec.rb +11 -0
- data/spec/rubix/monitors/cluster_monitor_spec.rb +11 -0
- data/spec/rubix/response_spec.rb +35 -0
- data/spec/rubix/sender_spec.rb +9 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/response_helper.rb +17 -0
- metadata +140 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'net/http'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'rubix/log'
|
6
|
+
|
7
|
+
module Rubix
|
8
|
+
|
9
|
+
class Connection
|
10
|
+
|
11
|
+
include Logs
|
12
|
+
|
13
|
+
attr_reader :uri, :server, :auth, :request_id, :username, :password
|
14
|
+
|
15
|
+
def initialize uri_or_string, username=nil, password=nil
|
16
|
+
self.uri = uri_or_string
|
17
|
+
@username = username || uri.user
|
18
|
+
@password = password || uri.password
|
19
|
+
@request_id = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
def uri= uri_or_string
|
23
|
+
if uri_or_string.respond_to?(:host)
|
24
|
+
@uri = uri_or_string
|
25
|
+
else
|
26
|
+
string = uri_or_string =~ /^http/ ? uri_or_string : 'http://' + uri_or_string.to_s
|
27
|
+
@uri = URI.parse(string)
|
28
|
+
end
|
29
|
+
@server = Net::HTTP.new(uri.host, uri.port)
|
30
|
+
end
|
31
|
+
|
32
|
+
def authorized?
|
33
|
+
!auth.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def authorization_params
|
37
|
+
{
|
38
|
+
:jsonrpc => "2.0",
|
39
|
+
:id => request_id,
|
40
|
+
:method => "user.login",
|
41
|
+
:params => {
|
42
|
+
:user => username,
|
43
|
+
:password => password
|
44
|
+
}
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def authorize!
|
49
|
+
response = till_response { send_raw_request(authorization_params) }
|
50
|
+
raise AuthenticationError.new("Could not authenticate with Zabbix API at #{uri}: #{response.error_message}") if response.error?
|
51
|
+
raise AuthenticationError.new("Malformed response from Zabbix API: #{response.body}") unless response.string?
|
52
|
+
@auth = response.result
|
53
|
+
end
|
54
|
+
|
55
|
+
def till_response attempt=1, max_attempts=5, &block
|
56
|
+
response = block.call
|
57
|
+
Rubix.logger.log(Logger::DEBUG, response.body, 'RECV') if Rubix.logger
|
58
|
+
case
|
59
|
+
when response.code.to_i >= 500 && attempt <= max_attempts
|
60
|
+
sleep 1 # FIXME make the sleep time configurable...
|
61
|
+
till_response(attempt + 1, max_attempts, &block)
|
62
|
+
when response.code.to_i >= 500
|
63
|
+
raise ConnectionError.new("Too many consecutive failed requests (#{max_attempts}) to the Zabbix API at (#{uri}).")
|
64
|
+
else
|
65
|
+
Response.new(response)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def request method, params
|
70
|
+
authorize! unless authorized?
|
71
|
+
till_response do
|
72
|
+
raw_params = {
|
73
|
+
:jsonrpc => "2.0",
|
74
|
+
:id => request_id,
|
75
|
+
:method => method,
|
76
|
+
:params => params,
|
77
|
+
:auth => auth
|
78
|
+
}
|
79
|
+
send_raw_request(raw_params)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def raw_post_request raw_params
|
84
|
+
json_body = raw_params.to_json
|
85
|
+
Rubix.logger.log(Logger::DEBUG, json_body, 'SEND') if Rubix.logger
|
86
|
+
Net::HTTP::Post.new(uri.path).tap do |req|
|
87
|
+
req['Content-Type'] = 'application/json-rpc'
|
88
|
+
req.body = json_body
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def host_with_port
|
93
|
+
if uri.port.nil? || uri.port.to_i == 80
|
94
|
+
uri.host
|
95
|
+
else
|
96
|
+
"#{uri.host}:#{uri.port}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def send_raw_request raw_params
|
101
|
+
@request_id += 1
|
102
|
+
begin
|
103
|
+
raw_response = server.request(raw_post_request(raw_params))
|
104
|
+
rescue NoMethodError, SocketError => e
|
105
|
+
raise RequestError.new("Could not connect to Zabbix server at #{host_with_port}")
|
106
|
+
end
|
107
|
+
raw_response
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
RUBIX_ROOT = File.expand_path('../../../../lib', __FILE__)
|
4
|
+
$: << RUBIX_ROOT unless $:.include?(RUBIX_ROOT)
|
5
|
+
|
6
|
+
require 'rubix'
|
7
|
+
require 'open-uri'
|
8
|
+
|
9
|
+
class ESMonitor < Rubix::ClusterMonitor
|
10
|
+
|
11
|
+
# Hostgroup for any hosts that needs to be created.
|
12
|
+
HOSTGROUP = 'Elasticsearch clusters'
|
13
|
+
|
14
|
+
# Templates for any hosts that need to be created.
|
15
|
+
CLUSTER_TEMPLATES = 'Template_Elasticsearch_Cluster'
|
16
|
+
NODE_TEMPLATES = 'Template_Elasticsearch_Node'
|
17
|
+
|
18
|
+
def node_query
|
19
|
+
'provides_service:*-elasticsearch'
|
20
|
+
end
|
21
|
+
|
22
|
+
def es_url private_ip, *args
|
23
|
+
"http://" + File.join(private_ip + ":9200", *args)
|
24
|
+
end
|
25
|
+
|
26
|
+
def measure_cluster cluster_name
|
27
|
+
measured_cluster_health = false
|
28
|
+
measured_cluster_indices = false
|
29
|
+
measured_cluster_nodes = false
|
30
|
+
private_ips_by_cluster[cluster_name].each do |private_ip|
|
31
|
+
measured_cluster_health = measure_cluster_health(cluster_name, private_ip) unless measured_cluster_health
|
32
|
+
measured_cluster_indices = measure_cluster_indices(cluster_name, private_ip) unless measured_cluster_indices
|
33
|
+
measured_cluster_nodes = measure_cluster_nodes(cluster_name, private_ip) unless measured_cluster_nodes
|
34
|
+
break if measured_cluster_health && measured_cluster_indices && measured_cluster_nodes
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Measure the cluster health metrics -- /_cluster/health
|
39
|
+
def measure_cluster_health cluster_name, private_ip
|
40
|
+
begin
|
41
|
+
cluster_health = JSON.parse(open(es_url(private_ip, '_cluster', 'health')).read)
|
42
|
+
rescue SocketError, OpenURI::HTTPError, JSON::ParserError, Errno::ECONNREFUSED => e
|
43
|
+
# This node may not be running a webnode...
|
44
|
+
return false
|
45
|
+
end
|
46
|
+
write({
|
47
|
+
:hostname => "#{cluster_name}-elasticsearch",
|
48
|
+
:application => "_cluster",
|
49
|
+
:templates => self.class::CLUSTER_TEMPLATES,
|
50
|
+
:hostgroup => self.class::HOSTGROUP
|
51
|
+
}) do |d|
|
52
|
+
d << ['status', cluster_health['status'] ]
|
53
|
+
d << ['nodes.total', cluster_health['number_of_nodes'] ]
|
54
|
+
d << ['nodes.data', cluster_health['number_of_data_nodes'] ]
|
55
|
+
d << ['shards.active', cluster_health['active_shards'] ]
|
56
|
+
d << ['shards.relocating', cluster_health['relocating_shards'] ]
|
57
|
+
d << ['shards.unassigned', cluster_health['unassigned_shards'] ]
|
58
|
+
d << ['shards.initializing', cluster_health['initializing_shards'] ]
|
59
|
+
end
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def measure_cluster_indices cluster_name, private_ip
|
64
|
+
begin
|
65
|
+
index_data = JSON.parse(open(es_url(private_ip, '_status')).read)
|
66
|
+
rescue SocketError, OpenURI::HTTPError, JSON::ParserError, Errno::ECONNREFUSED => e
|
67
|
+
# This node may not be running a webnode...
|
68
|
+
return false
|
69
|
+
end
|
70
|
+
index_data['indices'].each_pair do |index_name, index_data|
|
71
|
+
write({
|
72
|
+
:hostname => "#{cluster_name}-elasticsearch",
|
73
|
+
:appliation => index_name,
|
74
|
+
:templates => self.class::CLUSTER_TEMPLATES,
|
75
|
+
:hostgroup => self.class::HOSTGROUP
|
76
|
+
}) do |d|
|
77
|
+
d << ["#{index_name}.size", index_data["index"]["size_in_bytes"] ]
|
78
|
+
d << ["#{index_name}.docs.num", index_data["docs"]["num_docs"] ]
|
79
|
+
d << ["#{index_name}.docs.max", index_data["docs"]["max_doc"] ]
|
80
|
+
d << ["#{index_name}.docs.deleted", index_data["docs"]["deleted_docs"] ]
|
81
|
+
d << ["#{index_name}.operations", index_data["translog"]["operations"] ]
|
82
|
+
d << ["#{index_name}.merges.total", index_data["merges"]["total"] ]
|
83
|
+
d << ["#{index_name}.merges.current", index_data["merges"]["current"] ]
|
84
|
+
end
|
85
|
+
end
|
86
|
+
true
|
87
|
+
end
|
88
|
+
|
89
|
+
def measure_cluster_nodes cluster_name, private_ip
|
90
|
+
begin
|
91
|
+
nodes_data = JSON.parse(open(es_url(private_ip, '_cluster', 'nodes')).read)
|
92
|
+
nodes_stats_data = JSON.parse(open(es_url(private_ip, '_cluster', 'nodes', 'stats')).read)
|
93
|
+
rescue SocketError, OpenURI::HTTPError, JSON::ParserError, Errno::ECONNREFUSED => e
|
94
|
+
# This node may not be running a webnode...
|
95
|
+
return false
|
96
|
+
end
|
97
|
+
|
98
|
+
nodes_stats_data['nodes'].each_pair do |id, stats|
|
99
|
+
|
100
|
+
ip = nodes_data['nodes'][id]['network']['primary_interface']['address']
|
101
|
+
node_name = chef_node_name_from_ip(ip)
|
102
|
+
next unless node_name
|
103
|
+
write({
|
104
|
+
:hostname => node_name,
|
105
|
+
:application => "Elasticsearch",
|
106
|
+
:templates => self.class::NODE_TEMPLATES
|
107
|
+
}) do |d|
|
108
|
+
# concurrency
|
109
|
+
d << ['es.jvm.threads.count', stats['jvm']['threads']['count'] ]
|
110
|
+
|
111
|
+
# garbage collection
|
112
|
+
d << ['es.jvm.gc.coll_time', stats['jvm']['gc']['collection_time_in_millis'] ]
|
113
|
+
d << ['es.jvm.gc.coll_count', stats['jvm']['gc']['collection_count'] ]
|
114
|
+
|
115
|
+
# memory
|
116
|
+
d << ['es.jvm.mem.heap_used', stats['jvm']['mem']['heap_used_in_bytes'] ]
|
117
|
+
d << ['es.jvm.mem.non_heap_used', stats['jvm']['mem']['non_heap_used_in_bytes'] ]
|
118
|
+
d << ['es.jvm.mem.heap_comm', stats['jvm']['mem']['heap_committed_in_bytes'] ]
|
119
|
+
d << ['es.jvm.mem.non_heap_comm', stats['jvm']['mem']['non_heap_committed_in_bytes'] ]
|
120
|
+
|
121
|
+
# indices
|
122
|
+
d << ['es.indices.size', stats['indices']['size_in_bytes'] ]
|
123
|
+
end
|
124
|
+
end
|
125
|
+
true
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
ESMonitor.run if $0 == __FILE__
|
@@ -0,0 +1,87 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zabbix_cluster_monitor'
|
4
|
+
require 'net/http'
|
5
|
+
require 'crack'
|
6
|
+
|
7
|
+
class HBaseMonitor < ZabbixClusterMonitor
|
8
|
+
|
9
|
+
# Hostgroups for clusters & hosts that need to be created.
|
10
|
+
CLUSTER_HOSTGROUP = 'HBase clusters'
|
11
|
+
|
12
|
+
# Templates for any hosts that need to be created.
|
13
|
+
CLUSTER_TEMPLATES = 'Template_HBase_Cluster'
|
14
|
+
NODE_TEMPLATES = 'Template_HBase_Node'
|
15
|
+
|
16
|
+
def matching_chef_nodes
|
17
|
+
Chef::Search::Query.new.search('node', 'provides_service:*hbase-stargate AND facet_name:alpha')
|
18
|
+
end
|
19
|
+
|
20
|
+
def measure_cluster cluster_name
|
21
|
+
measured_cluster_status = false
|
22
|
+
private_ips_by_cluster[cluster_name].each do |private_ip|
|
23
|
+
measured_cluster_status = measure_cluster_status(cluster_name, private_ip) unless measured_cluster_status
|
24
|
+
break if measured_cluster_status
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Measure the cluster health metrics -- /status/cluster
|
29
|
+
def measure_cluster_status cluster_name, private_ip
|
30
|
+
begin
|
31
|
+
connection = Net::HTTP.new(private_ip, 8080) # FIXME port
|
32
|
+
request = Net::HTTP::Get.new('/status/cluster', 'Accept' => 'text/xml')
|
33
|
+
response = connection.request(request)
|
34
|
+
return false unless response.code.to_i == 200
|
35
|
+
|
36
|
+
data = Crack::XML.parse(response.body)
|
37
|
+
cluster_status = data['ClusterStatus']
|
38
|
+
dead_nodes = cluster_status['DeadNodes'] ? cluster_status['DeadNodes']['Node'] : []
|
39
|
+
live_nodes = cluster_status['LiveNodes']['Node']
|
40
|
+
rescue NoMethodError, SocketError, REXML::ParseException, Errno::ECONNREFUSED => e
|
41
|
+
# puts "#{e.class} -- #{e.message}"
|
42
|
+
# puts e.backtrace
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
|
46
|
+
write({
|
47
|
+
:hostname => "#{cluster_name}-hbase",
|
48
|
+
:application => '_cluster',
|
49
|
+
:hostgroup => 'HBase Clusters',
|
50
|
+
:templates => self.class::CLUSTER_TEMPLATES
|
51
|
+
}) do |d|
|
52
|
+
d << ['requests', cluster_status['requests']]
|
53
|
+
d << ['regions', cluster_status['regions']]
|
54
|
+
d << ['load', cluster_status['averageLoad']]
|
55
|
+
d << ['nodes.dead', dead_nodes.size]
|
56
|
+
d << ['nodes.alive', live_nodes.size]
|
57
|
+
end
|
58
|
+
measure_cluster_tables(cluster_name, data)
|
59
|
+
measure_cluster_nodes(cluster_name, live_nodes)
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
63
|
+
def measure_cluster_tables cluster_name, data
|
64
|
+
# FIXME...not sure how best to get information about "tables" in HBase...
|
65
|
+
end
|
66
|
+
|
67
|
+
def measure_cluster_nodes cluster_name, live_nodes
|
68
|
+
live_nodes.each do |live_node|
|
69
|
+
next unless live_node
|
70
|
+
ip = (live_node['name'] || '').split(':').first
|
71
|
+
node_name = chef_node_name_from_ip(ip)
|
72
|
+
next unless node_name
|
73
|
+
write({
|
74
|
+
:hostname => node_name,
|
75
|
+
:application => "HBase",
|
76
|
+
:templates => self.class::NODE_TEMPLATES
|
77
|
+
}) do |d|
|
78
|
+
d << ['hbase.regions', (live_node['Region'] || []).size]
|
79
|
+
d << ['hbase.heap_size', live_node['heapSizeMB']]
|
80
|
+
d << ['hbase.requests', live_node['requests']]
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
HBaseMonitor.run if $0 == __FILE__
|
@@ -0,0 +1,125 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'zabbix_cluster_monitor'
|
4
|
+
require 'open-uri'
|
5
|
+
require 'set'
|
6
|
+
require 'mongo'
|
7
|
+
|
8
|
+
class MongoMonitor < ZabbixClusterMonitor
|
9
|
+
|
10
|
+
# Hostgroup for any hosts that needs to be created.
|
11
|
+
HOSTGROUP = 'MongoDB clusters'
|
12
|
+
|
13
|
+
# Templates for any hosts that need to be created.
|
14
|
+
TEMPLATES = 'Template_MongoDB'
|
15
|
+
|
16
|
+
# Names of database to ignore when we find them.
|
17
|
+
IGNORED_DATABASES = %w[db test admin local].to_set
|
18
|
+
|
19
|
+
def matching_chef_nodes
|
20
|
+
Chef::Search::Query.new.search('node', 'provides_service:*-mongodb-server')
|
21
|
+
end
|
22
|
+
|
23
|
+
def measure_cluster cluster_name
|
24
|
+
measured_mongo_server = false
|
25
|
+
measured_mongo_databases = false
|
26
|
+
private_ips_by_cluster[cluster_name].each do |private_ip|
|
27
|
+
begin
|
28
|
+
connection = Mongo::Connection.new(private_ip)
|
29
|
+
rescue Mongo::ConnectionFailure => e
|
30
|
+
next
|
31
|
+
end
|
32
|
+
measured_mongo_server = measure_mongo_server(cluster_name, connection) unless measured_mongo_server
|
33
|
+
measured_mongo_databases = measure_mongo_databases(cluster_name, connection) unless measured_mongo_databases
|
34
|
+
break if measured_mongo_server && measured_mongo_databases
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def measure_mongo_server cluster_name, connection
|
39
|
+
initial = nil, final = nil
|
40
|
+
db = connection.db('system') # the name of this db doesn't matter?
|
41
|
+
command = {:serverStatus => true} # the value of the 'serverStatus' key doesn't matter?
|
42
|
+
|
43
|
+
# gather metrics with a 1.0 second gap
|
44
|
+
initial = db.command(command) ; sleep 1.0 ; final = db.command(command)
|
45
|
+
return false unless initial && final
|
46
|
+
dt = final['localTime'].to_f - initial['localTime'].to_f
|
47
|
+
|
48
|
+
write({
|
49
|
+
:hostname => "#{cluster_name}-mongodb",
|
50
|
+
:application => '_cluster',
|
51
|
+
:hostgroup => self.class::HOSTGROUP,
|
52
|
+
:templates => self.class::TEMPLATES
|
53
|
+
}) do |d|
|
54
|
+
|
55
|
+
# operations
|
56
|
+
d << ['inserts', (final['opcounters']['insert'] - initial['opcounters']['insert']) / dt]
|
57
|
+
d << ['queries', (final['opcounters']['query'] - initial['opcounters']['query']) / dt]
|
58
|
+
d << ['updates', (final['opcounters']['update'] - initial['opcounters']['update']) / dt]
|
59
|
+
d << ['deletes', (final['opcounters']['delete'] - initial['opcounters']['delete']) / dt]
|
60
|
+
d << ['getmores', (final['opcounters']['getmore'] - initial['opcounters']['getmore']) / dt]
|
61
|
+
d << ['commands', (final['opcounters']['command'] - initial['opcounters']['command']) / dt]
|
62
|
+
|
63
|
+
# memory
|
64
|
+
d << ['mem.resident', final['mem']['resident']]
|
65
|
+
d << ['mem.virtual', final['mem']['virtual']]
|
66
|
+
d << ['mem.mapped', final['mem']['mapped']]
|
67
|
+
|
68
|
+
# disk
|
69
|
+
d << ['flushes', (final['backgroundFlushing']['flushes'] - initial['backgroundFlushing']['flushes']) / dt]
|
70
|
+
d << ['flush_time', (final['backgroundFlushing']['total_ms'] - initial['backgroundFlushing']['total_ms']) ]
|
71
|
+
d << ['faults', (final['extra_info']['page_faults'] - initial['extra_info']['page_faults']) / dt]
|
72
|
+
|
73
|
+
# index
|
74
|
+
d << ['accesses', (final['indexCounters']['btree']['accesses'] - initial['indexCounters']['btree']['accesses']) / dt]
|
75
|
+
d << ['hits', (final['indexCounters']['btree']['hits'] - initial['indexCounters']['btree']['hits']) / dt]
|
76
|
+
d << ['misses', (final['indexCounters']['btree']['misses'] - initial['indexCounters']['btree']['misses']) / dt]
|
77
|
+
d << ['resets', (final['indexCounters']['btree']['resets'] - initial['indexCounters']['btree']['resets']) / dt]
|
78
|
+
|
79
|
+
# read/write load
|
80
|
+
d << ['queue.total', final['globalLock']['currentQueue']['total']]
|
81
|
+
d << ['queue.read', final['globalLock']['currentQueue']['readers']]
|
82
|
+
d << ['queue.write', final['globalLock']['currentQueue']['writers']]
|
83
|
+
d << ['clients.total', final['globalLock']['activeClients']['total']]
|
84
|
+
d << ['clients.read', final['globalLock']['activeClients']['readers']]
|
85
|
+
d << ['clients.write', final['globalLock']['activeClients']['writers']]
|
86
|
+
|
87
|
+
# network
|
88
|
+
d << ['net.in', (final['network']['bytesIn'] - initial['network']['bytesIn']) / dt]
|
89
|
+
d << ['net.out', (final['network']['bytesOut'] - initial['network']['bytesOut']) / dt]
|
90
|
+
d << ['requests', (final['network']['numRequests'] - initial['network']['numRequests']) / dt]
|
91
|
+
d << ['connections', final['connections']['current']]
|
92
|
+
end
|
93
|
+
true
|
94
|
+
end
|
95
|
+
|
96
|
+
def measure_mongo_databases cluster_name, connection
|
97
|
+
dbs = connection.database_names
|
98
|
+
return true if dbs.size == 0 # nothing to do here
|
99
|
+
|
100
|
+
dbs.each do |database_name|
|
101
|
+
next if self.class::IGNORED_DATABASES.include?(database_name.downcase)
|
102
|
+
stats = connection.db(database_name).stats()
|
103
|
+
|
104
|
+
write({
|
105
|
+
:hostname => "#{cluster_name}-mongodb",
|
106
|
+
:application => database_name,
|
107
|
+
:hostgroup => self.class::HOSTGROUP,
|
108
|
+
:templates => self.class::TEMPLATES
|
109
|
+
}) do |d|
|
110
|
+
d << ["#{database_name}.collections", stats["collections"] ]
|
111
|
+
d << ["#{database_name}.objects.count", stats["objects"] ]
|
112
|
+
d << ["#{database_name}.objects.avg_size", stats["avgObjSize"] ]
|
113
|
+
d << ["#{database_name}.size.data", stats["dataSize"] ]
|
114
|
+
d << ["#{database_name}.size.disk", stats["storageSize"] ]
|
115
|
+
d << ["#{database_name}.size.indexes", stats["indexSize"] ]
|
116
|
+
d << ["#{database_name}.size.file", stats["fileSize"] ]
|
117
|
+
d << ["#{database_name}.extents", stats["numExtents"] ]
|
118
|
+
d << ["#{database_name}.indexes", stats["indexes"] ]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
true
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
MongoMonitor.run if $0 == __FILE__
|