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.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/BUILD.md +50 -0
  3. data/CHANGELOG.md +5 -1
  4. data/CUSTOM_METRICS.md +9 -1
  5. data/INSTALL.md +89 -0
  6. data/LICENSE +1 -1
  7. data/README.md +23 -3
  8. data/Rakefile +257 -0
  9. data/TEST.md +9 -0
  10. data/bin/instrument_server +48 -7
  11. data/chef/.kitchen.yml +16 -0
  12. data/chef/Berksfile +4 -0
  13. data/chef/Berksfile.lock +9 -0
  14. data/chef/instrumental_tools/attributes/default.rb +2 -0
  15. data/chef/instrumental_tools/metadata.rb +3 -0
  16. data/chef/instrumental_tools/recipes/default.rb +21 -0
  17. data/chef/instrumental_tools/templates/instrumental.yml.erb +6 -0
  18. data/conf/instrumental.yml +6 -0
  19. data/debian/after-install.sh +6 -0
  20. data/debian/after-remove.sh +4 -0
  21. data/debian/before-remove.sh +4 -0
  22. data/debian/instrument_server +46 -0
  23. data/examples/mysql/mysql_status.rb +3 -3
  24. data/instrumental_tools.gemspec +32 -4
  25. data/lib/instrumental_tools/metric_script_executor.rb +38 -34
  26. data/lib/instrumental_tools/server_controller.rb +122 -35
  27. data/lib/instrumental_tools/system_inspector/linux.rb +80 -42
  28. data/lib/instrumental_tools/version.rb +1 -1
  29. data/puppet/.kitchen.yml +26 -0
  30. data/puppet/.librarian/puppet/config +2 -0
  31. data/puppet/Puppetfile +4 -0
  32. data/puppet/Puppetfile.lock +17 -0
  33. data/puppet/instrumental_tools/manifests/init.pp +29 -0
  34. data/puppet/instrumental_tools/metadata.json +11 -0
  35. data/puppet/instrumental_tools/templates/instrumental.yml.erb +6 -0
  36. data/puppet/manifests/site.pp +3 -0
  37. data/rpm/after-install.sh +7 -0
  38. data/rpm/after-remove.sh +4 -0
  39. data/rpm/before-remove.sh +5 -0
  40. data/rpm/instrument_server +46 -0
  41. data/systemd/instrument_server.service +13 -0
  42. data/test/integration/default/serverspec/instrumental_tools_spec.rb +29 -0
  43. metadata +164 -11
  44. 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,6 @@
1
+ ---
2
+ # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
+ # remove the # from the beginning of the following line to have instrument_server
4
+ # start sending metrics to your account.
5
+
6
+ api_key: <%= @api_key %>
@@ -0,0 +1,6 @@
1
+ ---
2
+ # Replace YOUR_API_KEY with the API key for your Instrumental project and
3
+ # remove the # from the beginning of the following line to have instrument_server
4
+ # start sending metrics to your account.
5
+
6
+ #api_key: YOUR_API_KEY
@@ -0,0 +1,6 @@
1
+ #!/bin/sh
2
+ set -e
3
+ update-rc.d instrument_server defaults
4
+ /etc/init.d/instrument_server start
5
+ echo "Remember to edit /etc/instrumental.yml with your Instrumental API key"
6
+ exit 0
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ set -e
3
+ update-rc.d -f instrument_server remove
4
+ exit 0
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ set -e
3
+ /etc/init.d/instrument_server stop
4
+ exit 0
@@ -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{Queries Bytes_sent Bytes_received Connections Slow_queries}
13
- CANARY_METRIC = "Queries"
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
@@ -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 = ["Elijah Miller", "Christopher Zelenak", "Kristopher Chambers", "Matthew Hassfurder"]
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 = `git ls-files`.split("\n")
14
- s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
- s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
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 run
27
- process_to_output = {}
28
- if can_execute_in_directory?(directory)
29
- current = Dir[File.join(directory, "*")].map do |path|
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
- previous_status, previous_time, previous_output = previous[full_path]
31
+ previous_status, previous_time, previous_output = previous[full_path]
37
32
 
38
- stdin_w.write(previous_output || "")
39
- stdin_w.close
33
+ stdin_w.write(previous_output || "")
34
+ stdin_w.close
40
35
 
41
36
 
42
- cmd = [full_path, (previous_time || 0).to_i, (previous_status && previous_status.to_i)].compact.map(&:to_s)
37
+ cmd = [full_path, (previous_time || 0).to_i, (previous_status && previous_status.to_i)].compact.map(&:to_s)
43
38
 
44
- pid = Process.spawn(*cmd,
45
- :chdir => File.dirname(full_path),
46
- :in => stdin_r,
47
- :out => stdout_w,
48
- :err => stderr_w)
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
- exit_status = nil
51
- exec_time = Benchmark.realtime do
52
- pid, exit_status = Process.wait2(pid)
53
- end
45
+ exit_status = nil
46
+ exec_time = Benchmark.realtime do
47
+ pid, exit_status = Process.wait2(pid)
48
+ end
54
49
 
55
- if exec_time > 1.0
56
- puts "[SLOW SCRIPT] Time to execute process #{full_path} took #{exec_time} seconds"
57
- end
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
- [stdin_r, stdout_w, stderr_w].each(&:close)
54
+ [stdin_r, stdout_w, stderr_w].each(&:close)
60
55
 
61
- output = stdout_r.read.to_s.chomp
56
+ output = stdout_r.read.to_s.chomp
62
57
 
63
- stderr = stderr_r.read.to_s.chomp
64
- unless stderr.empty?
65
- puts "[STDERR] #{full_path} (PID:#{pid}) [#{Time.now.to_s}]:: #{stderr}"
66
- end
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
- [stdout_r, stderr_r].each(&:close)
65
+ [full_path, [exit_status, Time.now, output]]
66
+ end
69
67
 
70
- [full_path, [exit_status, Time.now, output]]
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(/[^a-z0-9\-\_\.]/i, "_")
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 self.run(options)
28
- agent = Instrumental::Agent.new(options[:api_key], :collector => [options[:collector], options[:port]].compact.join(':'))
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: #{options[: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
- t = Time.now.to_i
35
- next_run_at = (t - t % report_interval) + report_interval
36
- sleep [next_run_at - t, 0].max
37
- inspector = SystemInspector.new
38
- inspector.load_all
39
- count = 0
40
- inspector.gauges.each do |stat, value|
41
- metric = "#{options[:hostname]}.#{stat}"
42
- agent.gauge(metric, value)
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
- categories = [:user, :nice, :system, :idle, :iowait]
12
- values = `cat /proc/stat | grep cpu[^0-9]`.chomp.split.slice(1, 5).map { |v| v.to_f }
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
- output = {}
22
- if previous_values
23
- data.each do |category, value|
24
- output["cpu.#{category}"] = value / total * 100
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
- output["cpu.in_use"] = 100 - data[:idle] / total * 100
58
+
28
59
  output
29
60
  end
30
61
 
31
62
  def self.loadavg
32
- min_1, min_5, min_15 = `cat /proc/loadavg`.split
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 SystemInspector.command_present?('free', 'memory')
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
- _, total, used, free, shared, buffers, cached = `free -k -o | grep Mem`.chomp.split
53
- {
54
- 'memory.used_mb' => used.to_f / 1024,
55
- 'memory.free_mb' => free.to_f / 1024,
56
- 'memory.buffers_mb' => buffers.to_f / 1024,
57
- 'memory.cached_mb' => cached.to_f / 1024,
58
- 'memory.free_percent' => (free.to_f / total.to_f) * 100
59
- }
60
- end
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 SystemInspector.command_present?('mount', 'disk IO')
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
- mounted_devices = `mount`.lines.grep(/^\/dev\/(\w+)/) { $1 }
108
- diskstats_lines = `cat /proc/diskstats`.lines.grep(/#{mounted_devices.join('|')}/)
109
- entries = diskstats_lines.map do |line|
110
- values = line.chomp.split
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 SystemInspector.command_present?('sysctl', 'filesystem')
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 = `sysctl fs.file-nr`.split[-3..-1].map { |v| v.to_i }
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,
@@ -1,5 +1,5 @@
1
1
  module Instrumental
2
2
  module Tools
3
- VERSION = "1.0.0.rc2"
3
+ VERSION = "1.0.0"
4
4
  end
5
5
  end
@@ -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