scout 5.9.7.2.pre → 5.9.8.pre

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 140399efe8eee3eba1aec08b48196a4f87140e31
4
- data.tar.gz: 3ba82c2aadc04ddace97c6d33ef1a63b728df903
3
+ metadata.gz: 194c916c84d7cc8a77e55c249f138e241e2bb640
4
+ data.tar.gz: 4aadaca0469ba3bddb08393264e39e528a9fc335
5
5
  SHA512:
6
- metadata.gz: c57acbf53039f8e08e19583939f1c29ba9759ae6d7f25f8f380b65284da397763710d845dc35857571f84969fc9445c550dd6cf1880aec6846629e5929d9dc40
7
- data.tar.gz: 6508b1e69bf1b2a6f1aee09950fe114b634abe1955b576fae2fdc4e97f6b59797dd88d4bb6ab69e2311ce321bb6ca3e30ac76224d89344ead1149ce9d5ac7b38
6
+ metadata.gz: 3ae8da04d1b7dff3e2c0fd7111c481b685d123dd2cd16ea3b97cae5a04a330d336904ca7909dd9421a8e96186a6410e14cbd2cdfd74954e6e56c491fc489c5a5
7
+ data.tar.gz: 19ef5795f05007fefb8f4a5fab15bdff62da3f9b54fdccd6759fd4196eeea3d740f6e775a6bedacf8be611162375ab0379a3d1cb02afb027f289a666541dc5b6
@@ -1,6 +1,6 @@
1
- # 5.9.7.2
1
+ # 5.9.8
2
2
 
3
- * Convert alert/error/report strings to UTF-8
3
+ * Added Nagios plugin support via the `--nagios` flag.
4
4
 
5
5
  # 5.9.7.1
6
6
 
@@ -9,6 +9,7 @@
9
9
  # 5.9.7
10
10
 
11
11
  * Preliminary support for realtime compatibility with GO agent
12
+ >>>>>>> master
12
13
 
13
14
  # 5.9.5
14
15
 
@@ -5,10 +5,12 @@ require "server_metrics"
5
5
 
6
6
  require "scout/version"
7
7
 
8
- require "scout/helpers"
9
8
  require "scout/http"
10
9
  require "scout/command"
11
10
  require "scout/plugin"
11
+ require "scout/third_party_plugins/third_party_plugins"
12
+ require "scout/third_party_plugins/munin_plugin"
13
+ require "scout/third_party_plugins/nagios_plugin"
12
14
  require "scout/plugin_options"
13
15
  require "scout/scout_logger"
14
16
  require "scout/server_base"
@@ -97,6 +97,14 @@ module Scout
97
97
  options[:environment] = environment
98
98
  end
99
99
 
100
+ opts.on( "--nagios [NRPE CONFIG PATH]", "Run Nagios plugins defined in the specified NRPE config file. If no path is provided, plugins are loaded from '/etc/nagios/nrpe.cfg'." ) do |nrpe_config_file_path|
101
+ options[:nrpe_config_file_path] = nrpe_config_file_path || '/etc/nagios/nrpe.cfg'
102
+ end
103
+
104
+ opts.on( "--munin [MUNIN PLUGIN PATH]", "Load Munin plugins from the specified path. If no path is provided, plugins are loaded from '/etc/munin/plugins'." ) do |munin_plugin_path|
105
+ options[:munin_plugin_path] = munin_plugin_path || '/etc/munin/plugins'
106
+ end
107
+
100
108
  opts.separator " "
101
109
  opts.separator "Common Options:"
102
110
  opts.separator "--------------------------------------------------------------------------"
@@ -184,6 +192,8 @@ module Scout
184
192
  @https_proxy = options[:https_proxy] || ""
185
193
  @hostname = options[:hostname] || Socket.gethostname
186
194
  @environment = options[:environment] || ""
195
+ @munin_plugin_path = options[:munin_plugin_path]
196
+ @nrpe_config_file_path = options[:nrpe_config_file_path]
187
197
  @options = options
188
198
  @args = args
189
199
 
@@ -10,7 +10,7 @@ module Scout
10
10
  log.debug("Running Scout [#{Scout::VERSION}] with server_metrics [#{ServerMetrics::VERSION}] on #{@hostname}") if log
11
11
  log.debug("Configuration directory is #{configuration_directory} ") if log
12
12
  # TODO: too much external logic of command doing things TO server. This should be moved into the server class.
13
- @scout = Scout::Server.new(server, key, history, log, server_name, @http_proxy, @https_proxy, @roles, @hostname, @environment)
13
+ @scout = Scout::Server.new(server, key, history, log, server_name, @http_proxy, @https_proxy, @roles, @hostname, @environment, @munin_plugin_path, @nrpe_config_file_path)
14
14
  @scout.load_history
15
15
 
16
16
  unless $stdin.tty?
@@ -116,17 +116,17 @@ module Scout
116
116
 
117
117
  if "#{kind}" == "report"
118
118
  def report(new_entry)
119
- reports << new_entry.convert_to_utf8
119
+ reports << new_entry
120
120
  end
121
121
  elsif "#{kind}" == "summary"
122
122
  def summary(new_entry)
123
- summaries << new_entry.convert_to_utf8
123
+ summaries << new_entry
124
124
  end
125
125
  else
126
126
  def #{kind}(*fields)
127
127
  #{kind}s << ( fields.first.is_a?(Hash) ?
128
- fields.first.convert_to_utf8 :
129
- {:subject => fields.first.convert_to_utf8, :body => fields.last.convert_to_utf8} )
128
+ fields.first :
129
+ {:subject => fields.first, :body => fields.last} )
130
130
  end
131
131
  end
132
132
  alias_method :add_#{kind}, :#{kind}
@@ -1,14 +1,13 @@
1
-
2
1
  Dir.glob(File.join(File.dirname(__FILE__), *%w[.. .. vendor *])).each do |dir|
3
2
  $LOAD_PATH << File.join(dir,"lib")
4
3
  end
5
-
6
4
  require "multi_json"
7
5
  require "pusher"
8
6
  require "httpclient"
9
7
 
10
8
  module Scout
11
9
  class Server < Scout::ServerBase
10
+ include ThirdPartyPlugins
12
11
  #
13
12
  # A plugin cannot take more than DEFAULT_PLUGIN_TIMEOUT seconds to execute,
14
13
  # otherwise, a timeout error is generated. This can be overriden by
@@ -28,7 +27,7 @@ module Scout
28
27
  attr_reader :client_key
29
28
 
30
29
  # Creates a new Scout Server connection.
31
- def initialize(server, client_key, history_file, logger=nil, server_name=nil, http_proxy='', https_proxy='', roles='', hostname=nil, environment='')
30
+ def initialize(server, client_key, history_file, logger=nil, server_name=nil, http_proxy='', https_proxy='', roles='', hostname=nil, environment='', munin_plugin_path, nrpe_config_file_path)
32
31
  @server = server
33
32
  @client_key = client_key
34
33
  @history_file = history_file
@@ -40,6 +39,8 @@ module Scout
40
39
  @roles = roles || ''
41
40
  @hostname = hostname
42
41
  @environment = environment
42
+ @munin_plugin_path = munin_plugin_path
43
+ @nrpe_config_file_path = nrpe_config_file_path
43
44
  @plugin_plan = []
44
45
  @plugins_with_signature_errors = []
45
46
  @directives = {} # take_snapshots, interval, sleep_interval
@@ -146,8 +147,8 @@ module Scout
146
147
 
147
148
  @new_plan = true # used in determination if we should checkin this time or not
148
149
 
149
- # Add local plugins to the plan.
150
150
  @plugin_plan += get_local_plugins
151
+ @plugin_plan += get_third_party_plugins
151
152
  rescue Exception =>e
152
153
  fatal "Plan from server was malformed: #{e.message} - #{e.backtrace}"
153
154
  exit
@@ -157,6 +158,7 @@ module Scout
157
158
  info "Plan not modified."
158
159
  @plugin_plan = Array(@history["old_plugins"])
159
160
  @plugin_plan += get_local_plugins
161
+ @plugin_plan += get_third_party_plugins
160
162
  @directives = @history["directives"] || Hash.new
161
163
 
162
164
  end
@@ -393,34 +395,40 @@ module Scout
393
395
  plugin['origin'] = nil
394
396
  end
395
397
  end
396
- debug "Compiling plugin..."
397
- begin
398
- eval( code_to_run,
399
- TOPLEVEL_BINDING,
400
- plugin['path'] || plugin['name'] )
401
- info "Plugin compiled."
402
- rescue Exception
403
- raise if $!.is_a? SystemExit
404
- error "Plugin #{plugin['path'] || plugin['name']} would not compile: #{$!.message}"
405
- @checkin[:errors] << build_report(plugin,:subject => "Plugin would not compile", :body=>"#{$!.message}\n\n#{$!.backtrace}")
406
- return
407
- end
398
+ if !third_party?(plugin)
399
+ debug "Compiling plugin..."
400
+ begin
401
+ eval( code_to_run,
402
+ TOPLEVEL_BINDING,
403
+ plugin['path'] || plugin['name'] )
404
+ info "Plugin compiled."
405
+ rescue Exception
406
+ raise if $!.is_a? SystemExit
407
+ error "Plugin #{plugin['path'] || plugin['name']} would not compile: #{$!.message}"
408
+ @checkin[:errors] << build_report(plugin,:subject => "Plugin would not compile", :body=>"#{$!.message}\n\n#{$!.backtrace}")
409
+ return
410
+ end
408
411
 
409
- # Lookup any local options in plugin_config.properies as needed
410
- options=(plugin['options'] || Hash.new)
411
- options.each_pair do |k,v|
412
- if v=~/^lookup:(.+)$/
413
- lookup_key = $1.strip
414
- if plugin_config[lookup_key]
415
- options[k]=plugin_config[lookup_key]
416
- else
417
- info "Plugin #{id_and_name}: option #{k} appears to be a lookup, but we can't find #{lookup_key} in #{@plugin_config_path}"
412
+ # Lookup any local options in plugin_config.properies as needed
413
+ options=(plugin['options'] || Hash.new)
414
+ options.each_pair do |k,v|
415
+ if v=~/^lookup:(.+)$/
416
+ lookup_key = $1.strip
417
+ if plugin_config[lookup_key]
418
+ options[k]=plugin_config[lookup_key]
419
+ else
420
+ info "Plugin #{id_and_name}: option #{k} appears to be a lookup, but we can't find #{lookup_key} in #{@plugin_config_path}"
421
+ end
418
422
  end
419
423
  end
424
+ elsif munin?(plugin)
425
+ Plugin.last_defined = MuninPlugin
426
+ elsif nagios?(plugin)
427
+ Plugin.last_defined = NagiosPlugin
420
428
  end
421
429
 
422
430
  debug "Loading plugin..."
423
- if job = Plugin.last_defined.load( last_run, (memory || Hash.new), options)
431
+ if job = third_party?(plugin) ? load_third_party(plugin) : Plugin.last_defined.load( last_run, (memory || Hash.new), options)
424
432
  info "Plugin loaded."
425
433
  debug "Running plugin..."
426
434
  begin
@@ -486,7 +494,8 @@ module Scout
486
494
  if Plugin.last_defined
487
495
  debug "Removing plugin code..."
488
496
  begin
489
- Object.send(:remove_const, Plugin.last_defined.to_s.split("::").first)
497
+ klasses = Plugin.last_defined.to_s.split("::")
498
+ Object.send(:remove_const, klasses.include?("Scout") ? klasses.last : klasses.first) # munin and nagios plugins have "Scout::Munin/Nagios". don't want to remove 'Scout'.
490
499
  Plugin.last_defined = nil
491
500
  info "Plugin Removed."
492
501
  rescue
@@ -0,0 +1,25 @@
1
+ module Scout
2
+ class MuninPlugin < Scout::Plugin
3
+ attr_accessor :file_name, :dir
4
+
5
+ # The file name of the munin plugin to run inside the munin plugins directory.
6
+ def initialize(options)
7
+ self.file_name = options['file_name']
8
+ self.dir = options['dir']
9
+ end
10
+
11
+ def build_report
12
+ output = IO.popen("cd #{dir};munin-run #{file_name}").readlines[0..19]
13
+ data = {}
14
+ output.each do |l|
15
+ # "i0.value 724\n"
16
+ match_data = l.match("^(.*).value\s(.*)$")
17
+ next if match_data.nil? # "multigraph diskstats_latency\n"
18
+ name = match_data[1]
19
+ value = match_data[2].to_f
20
+ data[name] = value
21
+ end
22
+ report(data)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,54 @@
1
+ module Scout
2
+ class NagiosPlugin < Scout::Plugin
3
+ attr_accessor :cmd
4
+
5
+ # The command, with arguments, to run a nagios plugin.
6
+ # Ex: /usr/lib/nagios/plugins/check_procs -w 150 -c 200
7
+ def initialize(cmd)
8
+ self.cmd = cmd
9
+ end
10
+
11
+ def build_report
12
+ return if !sanity_check
13
+
14
+ # We only support parsing the first line of nagios plugin output
15
+ IO.popen("#{cmd}") {|io| @nagios_output = io.readlines[0] }
16
+
17
+ # Use exit status integer for OK/WARN/ERROR/CRIT status
18
+ plugin_status = $?.exitstatus
19
+
20
+ data = parse_nagios_output(@nagios_output)
21
+ report(data.merge({:status => plugin_status}))
22
+ end
23
+
24
+ def sanity_check
25
+ match = cmd.match(/(\S+)/)
26
+ file = match[1].to_s
27
+ if !File.exists?(file)
28
+ error("The Nagios plugin file does not exist", "The file does not exist: #{file}.")
29
+ elsif !File.executable?(file)
30
+ error("Can not execute Nagios plugin", "The file is not executable: #{file}.")
31
+ end
32
+ data_for_server[:errors].any? ? false : true
33
+ end
34
+
35
+ def parse_nagios_output(output)
36
+ text_field, perf_field = output.split('|',2)
37
+ perf_data = {}
38
+ if !perf_field.nil? && perf_field.strip!.length
39
+ # Split the perf field
40
+ # 1) on spaces
41
+ # 2) up to the first 10 metrics
42
+ # 3) split each "k=v;;;;" formatted metric into a key and value
43
+ # 4) add the key to perf_data, and the digits from the value
44
+ perf_field.split(" ")[0,10].inject(perf_data) {|r,e| k,v=e.split('=')[0,2]; r[k] = v.slice!(/^[\d.]*/).to_f if k && v; r}
45
+ end
46
+
47
+ #TODO - Allow ability to define regex captures of the text field numerical values as metrics
48
+ text_data = {}
49
+
50
+ return perf_data.merge(text_data)
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,95 @@
1
+ # Abstracted logic for handling third-party plugins in the +Server+ class.
2
+ module ThirdPartyPlugins
3
+ # Returns true if the plugin hash is associated w/a 3rd-party plugin like Nagios or Munin.
4
+ def third_party?(hash)
5
+ self.munin?(hash) or self.nagios?(hash)
6
+ end
7
+
8
+ def munin?(hash)
9
+ hash['type'].to_s == 'MUNIN'
10
+ end
11
+
12
+ def nagios?(hash)
13
+ hash['type'].to_s == 'NAGIOS'
14
+ end
15
+
16
+ # Loading third-party plugins is simplier as they don't have options or memory.
17
+ def load_third_party(hash)
18
+ if munin?(hash)
19
+ MuninPlugin.new(hash['file_name'])
20
+ elsif nagios?(hash)
21
+ Scout::NagiosPlugin.new(hash['cmd'])
22
+ end
23
+ end
24
+
25
+ def get_third_party_plugins
26
+ (get_munin_plugins + get_nagios_plugins).compact
27
+ end
28
+
29
+ def get_munin_plugins
30
+ return [] unless @munin_plugin_path
31
+ munin_plugin_path=Dir.glob(File.join(@munin_plugin_path,"*"))
32
+ munin_plugin_path.map do |plugin_path|
33
+ name = File.basename(plugin_path)
34
+ options = if directives = @plugin_plan.find { |plugin| plugin['filename'] == name }
35
+ directives['options']
36
+ else
37
+ nil
38
+ end
39
+ begin
40
+ plugin = {
41
+ 'name' => name,
42
+ 'local_filename' => name,
43
+ 'origin' => 'LOCAL',
44
+ 'type' => 'MUNIN',
45
+ 'code' => name,
46
+ 'interval' => 0,
47
+ 'options' => options,
48
+ 'dir' => @munin_plugin_path
49
+ }
50
+ plugin
51
+ rescue => e
52
+ info "Error trying to read local plugin: #{plugin_path} -- #{e.backtrace.join('\n')}"
53
+ nil
54
+ end
55
+ end.compact
56
+ end
57
+
58
+ def get_nagios_plugins
59
+ return [] unless @nrpe_config_file_path
60
+ begin
61
+ nrpe_config = File.read(@nrpe_config_file_path)
62
+ rescue => e
63
+ info "Unable to read Nagios NRPE Config file [#{@nrpe_config_file_path}]: #{e.message}"
64
+ return []
65
+ end
66
+ commands = {}
67
+ nrpe_config.split("\n").each do |l|
68
+ # command[check_total_procs]=/usr/lib/nagios/plugins/check_procs -w 150 -c 200
69
+ # TODO - don't parse commands w/remote args. : command[check_load]=/usr/lib/nagios/plugins/check_load -w $ARG1$ -c $ARG2$
70
+ match = l.match(/(^command\[(.*)\]=)(.*)/)
71
+ if match
72
+ if match[3].include?('$ARG')
73
+ info "Skipping Nagios Command [#{match[2]}] as it contains remote arguments."
74
+ else
75
+ commands[match[2]] = match[3]
76
+ end
77
+ end
78
+ end
79
+ debug "Found #{commands.size} Nagios plugins"
80
+ # todo - ensure cmd file exists
81
+ plugins = []
82
+ commands.each do |name,cmd|
83
+ plugins << {
84
+ 'name' => name,
85
+ 'local_filename' => name,
86
+ 'origin' => 'LOCAL',
87
+ 'type' => 'NAGIOS',
88
+ 'code' => name,
89
+ 'interval' => 0,
90
+ 'cmd' => cmd # unique for nagios
91
+ }
92
+ end
93
+ plugins
94
+ end
95
+ end # ThirdPartyPlugins
@@ -1,3 +1,3 @@
1
1
  module Scout
2
- VERSION = "5.9.7.2.pre"
2
+ VERSION = "5.9.8.pre"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: scout
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.9.7.2.pre
4
+ version: 5.9.8.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andre Lewis
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2014-10-17 00:00:00.000000000 Z
13
+ date: 2014-10-16 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: elif
@@ -70,7 +70,6 @@ files:
70
70
  - lib/scout/daemon_spawn.rb
71
71
  - lib/scout/data_file.rb
72
72
  - lib/scout/environment.rb
73
- - lib/scout/helpers.rb
74
73
  - lib/scout/http.rb
75
74
  - lib/scout/plugin.rb
76
75
  - lib/scout/plugin_options.rb
@@ -79,6 +78,9 @@ files:
79
78
  - lib/scout/server_base.rb
80
79
  - lib/scout/streamer.rb
81
80
  - lib/scout/streamer_daemon.rb
81
+ - lib/scout/third_party_plugins/munin_plugin.rb
82
+ - lib/scout/third_party_plugins/nagios_plugin.rb
83
+ - lib/scout/third_party_plugins/third_party_plugins.rb
82
84
  - lib/scout/version.rb
83
85
  - scout.gemspec
84
86
  - test/plugins/disk_usage.rb
@@ -338,4 +340,8 @@ specification_version: 4
338
340
  summary: Scout is an easy-to-use hosted server monitoring service. The scout Ruby
339
341
  gem reports metrics to our service. The agent runs plugins, configured via the Scout
340
342
  web interface, to monitor a server.
341
- test_files: []
343
+ test_files:
344
+ - test/plugins/disk_usage.rb
345
+ - test/scout_test.rb
346
+ - test/streamer_test.rb
347
+ has_rdoc:
@@ -1,21 +0,0 @@
1
- class Hash
2
- def convert_to_utf8
3
- result_hash = {}
4
- self.each do |k,v|
5
- new_key = k.respond_to?(:convert_to_utf8) ? k.convert_to_utf8 : k
6
- new_value = v.respond_to?(:convert_to_utf8) ? v.convert_to_utf8 : v
7
- result_hash[new_key] = new_value
8
- end
9
- return result_hash
10
- end
11
- end
12
-
13
- class String
14
- def convert_to_utf8
15
- if self.respond_to?('encode') # We can convert natively using Ruby >1.9 encode()
16
- return self.encode('UTF-8', {:invalid => :replace, :undef => :replace, :replace => '?'})
17
- else # We can't convert natively, do nothing.
18
- return self
19
- end
20
- end
21
- end