scout 5.9.7.2.pre → 5.9.8.pre

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