instrumental_tools 1.0.0.rc2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/BUILD.md +50 -0
- data/CHANGELOG.md +5 -1
- data/CUSTOM_METRICS.md +9 -1
- data/INSTALL.md +89 -0
- data/LICENSE +1 -1
- data/README.md +23 -3
- data/Rakefile +257 -0
- data/TEST.md +9 -0
- data/bin/instrument_server +48 -7
- data/chef/.kitchen.yml +16 -0
- data/chef/Berksfile +4 -0
- data/chef/Berksfile.lock +9 -0
- data/chef/instrumental_tools/attributes/default.rb +2 -0
- data/chef/instrumental_tools/metadata.rb +3 -0
- data/chef/instrumental_tools/recipes/default.rb +21 -0
- data/chef/instrumental_tools/templates/instrumental.yml.erb +6 -0
- data/conf/instrumental.yml +6 -0
- data/debian/after-install.sh +6 -0
- data/debian/after-remove.sh +4 -0
- data/debian/before-remove.sh +4 -0
- data/debian/instrument_server +46 -0
- data/examples/mysql/mysql_status.rb +3 -3
- data/instrumental_tools.gemspec +32 -4
- data/lib/instrumental_tools/metric_script_executor.rb +38 -34
- data/lib/instrumental_tools/server_controller.rb +122 -35
- data/lib/instrumental_tools/system_inspector/linux.rb +80 -42
- data/lib/instrumental_tools/version.rb +1 -1
- data/puppet/.kitchen.yml +26 -0
- data/puppet/.librarian/puppet/config +2 -0
- data/puppet/Puppetfile +4 -0
- data/puppet/Puppetfile.lock +17 -0
- data/puppet/instrumental_tools/manifests/init.pp +29 -0
- data/puppet/instrumental_tools/metadata.json +11 -0
- data/puppet/instrumental_tools/templates/instrumental.yml.erb +6 -0
- data/puppet/manifests/site.pp +3 -0
- data/rpm/after-install.sh +7 -0
- data/rpm/after-remove.sh +4 -0
- data/rpm/before-remove.sh +5 -0
- data/rpm/instrument_server +46 -0
- data/systemd/instrument_server.service +13 -0
- data/test/integration/default/serverspec/instrumental_tools_spec.rb +29 -0
- metadata +164 -11
- data/.gitignore +0 -3
@@ -0,0 +1,21 @@
|
|
1
|
+
packagecloud_repo "expectedbehavior/instrumental" do
|
2
|
+
case node["platform_family"]
|
3
|
+
when "debian"
|
4
|
+
type "deb"
|
5
|
+
when "rhel"
|
6
|
+
type "rpm"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
package "instrumental-tools" do
|
11
|
+
action :upgrade
|
12
|
+
end
|
13
|
+
|
14
|
+
template "/etc/instrumental.yml" do
|
15
|
+
source "instrumental.yml.erb"
|
16
|
+
mode "0440"
|
17
|
+
owner "nobody"
|
18
|
+
variables(
|
19
|
+
:api_key => node[:instrumental][:api_key]
|
20
|
+
)
|
21
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#! /bin/sh
|
2
|
+
### BEGIN INIT INFO
|
3
|
+
# Provides: instrument_server
|
4
|
+
# Required-Start: $all
|
5
|
+
# Required-Stop: $all
|
6
|
+
# Default-Start: 2 3 4 5
|
7
|
+
# Default-Stop: 0 1 6
|
8
|
+
# Short-Description: Start instrument_server at boot to provide system metrics
|
9
|
+
# Description: Report system level metrics to the Instrumental service (https://instrumentalapp.com/)
|
10
|
+
### END INIT INFO
|
11
|
+
|
12
|
+
set -e
|
13
|
+
|
14
|
+
DIRECTORY="/opt/instrumental-tools/"
|
15
|
+
CONFIG_FILE="/etc/instrumental.yml"
|
16
|
+
TMPDIR=$DIRECTORY
|
17
|
+
PID="${DIRECTORY}instrument_server.pid"
|
18
|
+
LOG="${DIRECTORY}instrument_server.log"
|
19
|
+
SCRIPT_LOCATION="${DIRECTORY}.instrumental_scripts"
|
20
|
+
USER_TO_RUN_AS="nobody"
|
21
|
+
ARGS="-f ${CONFIG_FILE} -p ${PID} -l ${LOG} -s ${SCRIPT_LOCATION} -u ${USER_TO_RUN_AS} -t ${TMPDIR}"
|
22
|
+
PROCESS="${DIRECTORY}instrument_server ${ARGS}"
|
23
|
+
|
24
|
+
case "$1" in
|
25
|
+
start)
|
26
|
+
$PROCESS start
|
27
|
+
;;
|
28
|
+
stop)
|
29
|
+
$PROCESS stop
|
30
|
+
;;
|
31
|
+
restart)
|
32
|
+
$PROCESS restart
|
33
|
+
;;
|
34
|
+
status)
|
35
|
+
$PROCESS status
|
36
|
+
;;
|
37
|
+
force-reload)
|
38
|
+
$PROCESS stop && $PROCESS clean && $PROCESS start
|
39
|
+
;;
|
40
|
+
*)
|
41
|
+
echo "Usage: /etc/init.d/instrumental-tools {start|stop|restart|status}"
|
42
|
+
exit 1
|
43
|
+
;;
|
44
|
+
esac
|
45
|
+
|
46
|
+
exit 0
|
@@ -9,8 +9,8 @@ MYSQL_USER = ENV["MYSQL_USER"]
|
|
9
9
|
MYSQL_DEFAULTS_FILE = ENV["MYSQL_DEFAULTS_FILE"]
|
10
10
|
MYSQL_PASSWORD = ENV["MYSQL_PASSWORD"]
|
11
11
|
|
12
|
-
RATE_METRICS_TO_INSPECT = %w{
|
13
|
-
CANARY_METRIC = "
|
12
|
+
RATE_METRICS_TO_INSPECT = %w{queries bytes_sent bytes_received connections slow_queries}
|
13
|
+
CANARY_METRIC = "queries"
|
14
14
|
|
15
15
|
env = {}
|
16
16
|
args = []
|
@@ -61,7 +61,7 @@ if !exit_status.success?
|
|
61
61
|
else
|
62
62
|
output = stdout_r.read.lines # each line
|
63
63
|
.map { |line| line.chomp.split } # split by space characters
|
64
|
-
.map { |(name, value, _)| [name, value.to_f] } # with values coerced to floats
|
64
|
+
.map { |(name, value, _)| [name.downcase, value.to_f] } # with values coerced to floats
|
65
65
|
stats = Hash[output]
|
66
66
|
if (stats[CANARY_METRIC] < previous_values[CANARY_METRIC].to_i) || previous_values[CANARY_METRIC].nil?
|
67
67
|
# The server has restarted, don't trust previous values for calculating difference
|
data/instrumental_tools.gemspec
CHANGED
@@ -1,18 +1,37 @@
|
|
1
1
|
$: << "./lib"
|
2
|
+
require 'find'
|
2
3
|
require 'instrumental_tools/version'
|
3
4
|
|
5
|
+
gitignore = Array(File.exists?(".gitignore") ? File.read(".gitignore").split("\n") : []) + [".git", ".gitignore"]
|
6
|
+
all_files = []
|
7
|
+
|
8
|
+
Find.find(".") do |path|
|
9
|
+
scrubbed_path = path.gsub(/\A\.\//, "")
|
10
|
+
if gitignore.any? { |glob| File.fnmatch(glob, scrubbed_path) }
|
11
|
+
Find.prune
|
12
|
+
else
|
13
|
+
if !File.directory?(scrubbed_path)
|
14
|
+
all_files << scrubbed_path
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
test_files = all_files.select { |path| path =~ /\A(test|spec|features)\//i }
|
20
|
+
bin_files = all_files.select { |path| path.index("bin") == 0 }.map { |path| File.basename(path) }
|
21
|
+
|
4
22
|
Gem::Specification.new do |s|
|
5
23
|
s.name = "instrumental_tools"
|
6
24
|
s.version = Instrumental::Tools::VERSION
|
7
|
-
s.authors = ["
|
25
|
+
s.authors = ["Expected Behavior"]
|
8
26
|
s.email = ["support@instrumentalapp.com"]
|
9
27
|
s.homepage = "http://github.com/expectedbehavior/instrumental_tools"
|
10
28
|
s.summary = %q{Command line tools for Instrumental}
|
11
29
|
s.description = %q{A collection of scripts useful for monitoring servers and services with Instrumental (instrumentalapp.com)}
|
30
|
+
s.licenses = ["MIT"]
|
12
31
|
|
13
|
-
s.files =
|
14
|
-
s.test_files =
|
15
|
-
s.executables =
|
32
|
+
s.files = all_files
|
33
|
+
s.test_files = test_files
|
34
|
+
s.executables = bin_files
|
16
35
|
s.require_paths = ["lib"]
|
17
36
|
|
18
37
|
s.required_ruby_version = '>= 1.9'
|
@@ -20,4 +39,13 @@ Gem::Specification.new do |s|
|
|
20
39
|
s.add_runtime_dependency(%q<instrumental_agent>, [">=0.12.6"])
|
21
40
|
s.add_runtime_dependency(%q<pidly>, [">=0.1.3"])
|
22
41
|
s.add_development_dependency(%q<rake>, [">=0"])
|
42
|
+
s.add_development_dependency(%q<fpm>, [">=1.3.3"])
|
43
|
+
s.add_development_dependency(%q<package_cloud>, [">=0"])
|
44
|
+
s.add_development_dependency(%q<test-kitchen>, [">=0"])
|
45
|
+
s.add_development_dependency(%q<kitchen-vagrant>, [">=0"])
|
46
|
+
s.add_development_dependency(%q<kitchen-puppet>, [">=0"])
|
47
|
+
s.add_development_dependency(%q<berkshelf>, [">=0"])
|
48
|
+
s.add_development_dependency(%q<librarian-puppet>, [">=0"])
|
49
|
+
s.add_development_dependency(%q<puppet>, [">=0"])
|
50
|
+
s.add_development_dependency(%q<serverspec>, [">=0"])
|
23
51
|
end
|
@@ -23,51 +23,55 @@ class MetricScriptExecutor
|
|
23
23
|
file_stat.owned? && ((file_stat.mode & 0xFFF) ^ 0O700) == 0
|
24
24
|
end
|
25
25
|
|
26
|
-
def
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
full_path = File.expand_path(path)
|
31
|
-
if can_execute_file?(path)
|
32
|
-
stdin_r, stdin_w = IO.pipe
|
33
|
-
stdout_r, stdout_w = IO.pipe
|
34
|
-
stderr_r, stderr_w = IO.pipe
|
26
|
+
def execute_custom_script(full_path)
|
27
|
+
stdin_r, stdin_w = IO.pipe
|
28
|
+
stdout_r, stdout_w = IO.pipe
|
29
|
+
stderr_r, stderr_w = IO.pipe
|
35
30
|
|
36
|
-
|
31
|
+
previous_status, previous_time, previous_output = previous[full_path]
|
37
32
|
|
38
|
-
|
39
|
-
|
33
|
+
stdin_w.write(previous_output || "")
|
34
|
+
stdin_w.close
|
40
35
|
|
41
36
|
|
42
|
-
|
37
|
+
cmd = [full_path, (previous_time || 0).to_i, (previous_status && previous_status.to_i)].compact.map(&:to_s)
|
43
38
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
39
|
+
pid = Process.spawn(*cmd,
|
40
|
+
:chdir => File.dirname(full_path),
|
41
|
+
:in => stdin_r,
|
42
|
+
:out => stdout_w,
|
43
|
+
:err => stderr_w)
|
49
44
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
45
|
+
exit_status = nil
|
46
|
+
exec_time = Benchmark.realtime do
|
47
|
+
pid, exit_status = Process.wait2(pid)
|
48
|
+
end
|
54
49
|
|
55
|
-
|
56
|
-
|
57
|
-
|
50
|
+
if exec_time > 1.0
|
51
|
+
puts "[SLOW SCRIPT] Time to execute process #{full_path} took #{exec_time} seconds"
|
52
|
+
end
|
58
53
|
|
59
|
-
|
54
|
+
[stdin_r, stdout_w, stderr_w].each(&:close)
|
60
55
|
|
61
|
-
|
56
|
+
output = stdout_r.read.to_s.chomp
|
62
57
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
58
|
+
stderr = stderr_r.read.to_s.chomp
|
59
|
+
unless stderr.empty?
|
60
|
+
puts "[STDERR] #{full_path} (PID:#{pid}) [#{Time.now.to_s}]:: #{stderr}"
|
61
|
+
end
|
62
|
+
|
63
|
+
[stdout_r, stderr_r].each(&:close)
|
67
64
|
|
68
|
-
|
65
|
+
[full_path, [exit_status, Time.now, output]]
|
66
|
+
end
|
69
67
|
|
70
|
-
|
68
|
+
def run
|
69
|
+
process_to_output = {}
|
70
|
+
if can_execute_in_directory?(directory)
|
71
|
+
current = Dir[File.join(directory, "*")].map do |path|
|
72
|
+
full_path = File.expand_path(path)
|
73
|
+
if can_execute_file?(path)
|
74
|
+
execute_custom_script(full_path)
|
71
75
|
else
|
72
76
|
if !File.directory?(full_path)
|
73
77
|
uid = Process.uid
|
@@ -84,7 +88,7 @@ class MetricScriptExecutor
|
|
84
88
|
end
|
85
89
|
process_to_output.flat_map do |path, (status, time, output)|
|
86
90
|
if status && status.success?
|
87
|
-
prefix = File.basename(path).split(".")[0..-2].join(".").gsub(/[
|
91
|
+
prefix = File.basename(path).split(".")[0..-2].join(".").gsub(/[^\d\w\-\_\.]/i, "_")
|
88
92
|
output.lines # each line
|
89
93
|
.map { |line| line.chomp.split } # split by whitespace
|
90
94
|
.select { |data| (2..3).include?(data.size) } # and only valid name value time? pairs
|
@@ -1,11 +1,13 @@
|
|
1
|
-
require 'pidly'
|
2
1
|
require 'instrumental_tools/metric_script_executor'
|
3
2
|
require 'instrumental_tools/system_inspector'
|
3
|
+
require 'pidly'
|
4
|
+
require 'yaml'
|
4
5
|
|
5
6
|
class ServerController < Pidly::Control
|
6
7
|
COMMANDS = [:start, :stop, :status, :restart, :clean, :kill, :foreground]
|
7
8
|
|
8
9
|
attr_accessor :run_options, :pid
|
10
|
+
attr_reader :current_api_key
|
9
11
|
|
10
12
|
before_start do
|
11
13
|
extra_info = if run_options[:daemon]
|
@@ -24,49 +26,134 @@ class ServerController < Pidly::Control
|
|
24
26
|
puts 'Error encountered'
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
+
def initialize(options={})
|
30
|
+
@run_options = options.delete(:run_options) || {}
|
31
|
+
super(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def foreground
|
35
|
+
run
|
36
|
+
end
|
37
|
+
|
38
|
+
def collector_address
|
39
|
+
[run_options[:collector], run_options[:port]].compact.join(':')
|
40
|
+
end
|
41
|
+
|
42
|
+
def user_specified_api_key
|
43
|
+
run_options[:api_key]
|
44
|
+
end
|
45
|
+
|
46
|
+
def config_file_api_key
|
47
|
+
if config_file_available?
|
48
|
+
config_contents = YAML.load(File.read(run_options[:config_file]))
|
49
|
+
if config_contents.is_a?(Hash)
|
50
|
+
config_contents['api_key']
|
51
|
+
end
|
52
|
+
end
|
53
|
+
rescue Exception => e
|
54
|
+
puts "Error loading config file %s: %s" % [run_options[:config_file], e.message]
|
55
|
+
nil
|
56
|
+
end
|
57
|
+
|
58
|
+
def configured_api_key
|
59
|
+
(user_specified_api_key || config_file_api_key).to_s.strip
|
60
|
+
end
|
61
|
+
|
62
|
+
def build_agent(key, address, enabled)
|
63
|
+
Instrumental::Agent.new(key, collector: address, enabled: enabled)
|
64
|
+
end
|
65
|
+
|
66
|
+
def set_new_agent(key, address)
|
67
|
+
key = key.to_s.strip
|
68
|
+
@current_api_key = key
|
69
|
+
@agent = build_agent(key, collector_address, key.size > 0)
|
70
|
+
end
|
71
|
+
|
72
|
+
def agent
|
73
|
+
if key_has_changed?
|
74
|
+
set_new_agent(configured_api_key, collector_address)
|
75
|
+
end
|
76
|
+
@agent
|
77
|
+
end
|
78
|
+
|
79
|
+
def report_interval
|
80
|
+
run_options[:report_interval]
|
81
|
+
end
|
82
|
+
|
83
|
+
def hostname
|
84
|
+
run_options[:hostname]
|
85
|
+
end
|
86
|
+
|
87
|
+
def script_location
|
88
|
+
run_options[:script_location]
|
89
|
+
end
|
90
|
+
|
91
|
+
def script_executor
|
92
|
+
@executor ||= MetricScriptExecutor.new(script_location)
|
93
|
+
end
|
94
|
+
|
95
|
+
def next_run_at(at_moment = Time.now.to_i)
|
96
|
+
(at_moment - at_moment % report_interval) + report_interval
|
97
|
+
end
|
98
|
+
|
99
|
+
def time_to_sleep
|
100
|
+
t = Time.now.to_i
|
101
|
+
[next_run_at(t) - t, 0].max
|
102
|
+
end
|
103
|
+
|
104
|
+
def config_file_available?
|
105
|
+
File.exists?(run_options[:config_file])
|
106
|
+
end
|
107
|
+
|
108
|
+
def enabled?
|
109
|
+
agent.enabled
|
110
|
+
end
|
111
|
+
|
112
|
+
def debug?
|
113
|
+
!!run_options[:debug]
|
114
|
+
end
|
115
|
+
|
116
|
+
def enable_scripts?
|
117
|
+
!!run_options[:enable_scripts]
|
118
|
+
end
|
119
|
+
|
120
|
+
def key_has_changed?
|
121
|
+
current_api_key != configured_api_key
|
122
|
+
end
|
123
|
+
|
124
|
+
def run
|
29
125
|
puts "instrument_server version #{Instrumental::Tools::VERSION} started at #{Time.now.utc}"
|
30
|
-
puts "Collecting stats under the hostname: #{
|
31
|
-
report_interval = options[:report_interval]
|
32
|
-
custom_metrics = MetricScriptExecutor.new(options[:script_location])
|
126
|
+
puts "Collecting stats under the hostname: #{hostname}"
|
33
127
|
loop do
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
if options[:debug]
|
44
|
-
puts [metric, value].join(":")
|
45
|
-
end
|
46
|
-
count += 1
|
47
|
-
end
|
48
|
-
if options[:enable_scripts]
|
49
|
-
custom_metrics.run.each do |(stat, value, time)|
|
50
|
-
metric = "#{options[:hostname]}.#{stat}"
|
51
|
-
agent.gauge(metric, value, time)
|
52
|
-
if options[:debug]
|
128
|
+
sleep time_to_sleep
|
129
|
+
if enabled?
|
130
|
+
inspector = SystemInspector.new
|
131
|
+
inspector.load_all
|
132
|
+
count = 0
|
133
|
+
inspector.gauges.each do |stat, value|
|
134
|
+
metric = [hostname, stat].join(".")
|
135
|
+
agent.gauge(metric, value)
|
136
|
+
if debug?
|
53
137
|
puts [metric, value].join(":")
|
54
138
|
end
|
55
139
|
count += 1
|
56
140
|
end
|
141
|
+
if enable_scripts?
|
142
|
+
script_executor.run.each do |(stat, value, time)|
|
143
|
+
metric = [hostname, stat].join(".")
|
144
|
+
agent.gauge(metric, value, time)
|
145
|
+
if debug?
|
146
|
+
puts [metric, value].join(":")
|
147
|
+
end
|
148
|
+
count += 1
|
149
|
+
end
|
150
|
+
end
|
151
|
+
if debug?
|
152
|
+
puts "Sent #{count} metrics"
|
153
|
+
end
|
57
154
|
end
|
58
|
-
puts "Sent #{count} metrics"
|
59
155
|
end
|
60
156
|
end
|
61
157
|
|
62
|
-
def initialize(options={})
|
63
|
-
@run_options = options.delete(:run_options) || {}
|
64
|
-
super(options)
|
65
|
-
end
|
66
|
-
|
67
|
-
def foreground
|
68
|
-
self.class.run(run_options)
|
69
|
-
end
|
70
|
-
|
71
158
|
alias_method :clean, :clean!
|
72
159
|
end
|
@@ -7,29 +7,60 @@ class SystemInspector
|
|
7
7
|
output
|
8
8
|
end
|
9
9
|
|
10
|
+
def self.cpu_file
|
11
|
+
"/proc/stat"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.load_file
|
15
|
+
"/proc/loadavg"
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.disk_file
|
19
|
+
"/proc/diskstats"
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.mount_file
|
23
|
+
"/proc/mounts"
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.open_files_file
|
27
|
+
"/proc/sys/fs/file-nr"
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.memory_file
|
31
|
+
"/proc/meminfo"
|
32
|
+
end
|
33
|
+
|
10
34
|
def self.cpu
|
11
|
-
|
12
|
-
|
13
|
-
SystemInspector.memory.store(:cpu_values, values.dup)
|
14
|
-
if previous_values = SystemInspector.memory.retrieve(:cpu_values)
|
15
|
-
index = -1
|
16
|
-
values.collect! { |value| (previous_values[index += 1] - value).abs }
|
17
|
-
end
|
18
|
-
data = Hash[*categories.zip(values).flatten]
|
19
|
-
total = values.inject { |memo, value| memo + value }
|
35
|
+
agg_cpu_stat = File.read(cpu_file).lines.map { |line| line.split }.detect { |values| values.first == "cpu" }
|
36
|
+
output = {}
|
20
37
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
38
|
+
if agg_cpu_stat
|
39
|
+
categories = [:user, :nice, :system, :idle, :iowait]
|
40
|
+
values = agg_cpu_stat.slice(1, 5).map { |v| v.to_f }
|
41
|
+
SystemInspector.memory.store(:cpu_values, values.dup)
|
42
|
+
if previous_values = SystemInspector.memory.retrieve(:cpu_values)
|
43
|
+
index = -1
|
44
|
+
values.collect! { |value| (previous_values[index += 1] - value).abs }
|
25
45
|
end
|
46
|
+
|
47
|
+
data = Hash[*categories.zip(values).flatten]
|
48
|
+
total = values.inject { |memo, value| memo + value }
|
49
|
+
|
50
|
+
if previous_values
|
51
|
+
data.each do |category, value|
|
52
|
+
output["cpu.#{category}"] = value / total * 100
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
output["cpu.in_use"] = 100 - data[:idle] / total * 100
|
26
57
|
end
|
27
|
-
|
58
|
+
|
28
59
|
output
|
29
60
|
end
|
30
61
|
|
31
62
|
def self.loadavg
|
32
|
-
min_1, min_5, min_15 =
|
63
|
+
min_1, min_5, min_15 = File.read(load_file).split
|
33
64
|
{
|
34
65
|
'load.1min' => min_1.to_f,
|
35
66
|
'load.5min' => min_5.to_f,
|
@@ -39,34 +70,41 @@ class SystemInspector
|
|
39
70
|
|
40
71
|
def self.load_memory
|
41
72
|
output = { :gauges => {} }
|
42
|
-
if
|
73
|
+
if File.exists?(memory_file)
|
43
74
|
output[:gauges].merge!(memory)
|
44
75
|
end
|
45
|
-
if SystemInspector.command_present?('free', 'swap')
|
46
|
-
output[:gauges].merge!(swap)
|
47
|
-
end
|
48
76
|
output
|
49
77
|
end
|
50
78
|
|
51
79
|
def self.memory
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
80
|
+
memory_stats = Hash[File.read(memory_file).lines.map { |line| line.chomp.strip.split(/:\s+/) }.reject { |l| l.size != 2 } ]
|
81
|
+
total = memory_stats["MemTotal"].to_f
|
82
|
+
free = memory_stats["MemFree"].to_f
|
83
|
+
used = total - free
|
84
|
+
buffers = memory_stats["Buffers"].to_f
|
85
|
+
cached = memory_stats["Cached"].to_f
|
86
|
+
swaptotal = memory_stats["SwapTotal"].to_f
|
87
|
+
swapfree = memory_stats["SwapFree"].to_f
|
88
|
+
swapused = swaptotal - swapfree
|
89
|
+
|
90
|
+
stats_to_record = {
|
91
|
+
'memory.used_mb' => used / 1024,
|
92
|
+
'memory.free_mb' => free / 1024,
|
93
|
+
'memory.buffers_mb' => buffers / 1024,
|
94
|
+
'memory.cached_mb' => cached / 1024,
|
95
|
+
'memory.free_percent' => (free / total) * 100,
|
61
96
|
|
62
|
-
def self.swap
|
63
|
-
_, total, used, free = `free -k -o | grep Swap`.chomp.split
|
64
|
-
return {} if total.to_i == 0
|
65
|
-
{
|
66
|
-
'swap.used_mb' => used.to_f / 1024,
|
67
|
-
'swap.free_mb' => free.to_f / 1024,
|
68
|
-
'swap.free_percent' => (free.to_f / total.to_f) * 100
|
69
97
|
}
|
98
|
+
|
99
|
+
if swaptotal > 0
|
100
|
+
stats_to_record.merge!({
|
101
|
+
'swap.used_mb' => swapused / 1024,
|
102
|
+
'swap.free_mb' => swapfree / 1024,
|
103
|
+
'swap.free_percent' => (swapfree / swaptotal) * 100
|
104
|
+
})
|
105
|
+
end
|
106
|
+
|
107
|
+
stats_to_record
|
70
108
|
end
|
71
109
|
|
72
110
|
def self.load_disks
|
@@ -74,7 +112,7 @@ class SystemInspector
|
|
74
112
|
if SystemInspector.command_present?('df', 'disk storage')
|
75
113
|
output[:gauges].merge!(disk_storage)
|
76
114
|
end
|
77
|
-
if
|
115
|
+
if File.exists?(mount_file) && File.exists?(disk_file)
|
78
116
|
output[:gauges].merge!(disk_io)
|
79
117
|
end
|
80
118
|
output
|
@@ -104,10 +142,10 @@ class SystemInspector
|
|
104
142
|
|
105
143
|
def self.disk_io
|
106
144
|
output = {}
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
145
|
+
device_root = "/dev/"
|
146
|
+
mounted_devices = File.read(mount_file).lines.map { |l| l.split.first }.select { |device| device.index(device_root) }.map { |device| File.realpath(device) }
|
147
|
+
diskstats_lines = File.read(disk_file).lines.map(&:split).select { |values| mounted_devices.include?(File.join(device_root, values[2])) }
|
148
|
+
entries = diskstats_lines.map do |values|
|
111
149
|
entry = {}
|
112
150
|
entry[:time] = Time.now
|
113
151
|
entry[:device] = values[2]
|
@@ -127,14 +165,14 @@ class SystemInspector
|
|
127
165
|
|
128
166
|
def self.load_filesystem
|
129
167
|
output = { :gauges => {} }
|
130
|
-
if
|
168
|
+
if File.exists?(open_files_file)
|
131
169
|
output[:gauges].merge!(filesystem)
|
132
170
|
end
|
133
171
|
output
|
134
172
|
end
|
135
173
|
|
136
174
|
def self.filesystem
|
137
|
-
allocated, unused, max =
|
175
|
+
allocated, unused, max = File.read(open_files_file).split.map(&:to_i)
|
138
176
|
open_files = allocated - unused
|
139
177
|
{
|
140
178
|
'filesystem.open_files' => open_files,
|
data/puppet/.kitchen.yml
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
---
|
2
|
+
driver:
|
3
|
+
name: vagrant
|
4
|
+
provider: vmware_fusion
|
5
|
+
|
6
|
+
provisioner:
|
7
|
+
name: puppet_apply
|
8
|
+
manifests_path: manifests
|
9
|
+
modules_path: modules
|
10
|
+
hiera_data_path: hieradata
|
11
|
+
|
12
|
+
platforms:
|
13
|
+
- name: nocm_ubuntu-12.04
|
14
|
+
driver_plugin: vagrant
|
15
|
+
driver_config:
|
16
|
+
box: nocm_ubuntu-12.04
|
17
|
+
box_url: https://atlas.hashicorp.com/puppetlabs/boxes/ubuntu-12.04-64-nocm/versions/1.0.1/providers/vmware_fusion.box
|
18
|
+
- name: nocm_centos-6.6
|
19
|
+
driver_plugin: vagrant
|
20
|
+
driver_config:
|
21
|
+
box: nocm_centos-6.6
|
22
|
+
box_url: https://atlas.hashicorp.com/puppetlabs/boxes/centos-6.6-64-nocm/versions/1.0.1/providers/vmware_fusion.box
|
23
|
+
|
24
|
+
suites:
|
25
|
+
- name: default
|
26
|
+
manifest: site.pp
|