class Panoptimon::Monitor

Attributes

bus[R]
cached[R]
collectors[R]
config[R]
loaded_plugins[R]
owd[R]
plugins[R]

Public Class Methods

new(args={}) click to toggle source
# File lib/panoptimon/monitor.rb, line 11
def initialize (args={})
  @collectors = []
  @plugins    = {}
  @loaded_plugins = {}
  args.each { |k,v| instance_variable_set("@#{k}", v) }

  @owd = Dir.pwd

  me = self
  @bus = EM.spawn { |metric| me.bus_driver(metric) }
end

Public Instance Methods

_autodetect_collector_command_path(name) click to toggle source

Searches for ‘pancollect-’ executables in $PATH Returns nil if no command found

# File lib/panoptimon/monitor.rb, line 73
def _autodetect_collector_command_path(name)
  pathdirs = ENV["PATH"].split(":")
  name = 'pancollect-' + name
  pathdirs.each{|basepath|
    path = File.join(basepath, name)
    logger.debug "checking path #{path}"
    return path if File.exists?(path)
  }
  return nil
end
_dirjson(x) click to toggle source

Search directories for JSON files

# File lib/panoptimon/monitor.rb, line 24
def _dirjson (x)
  x = Pathname.new(x)
  x.entries.find_all {|f| f.to_s =~ /\.json$/i}.
    map {|f| x + f}
end
_init_collector(conf) click to toggle source
# File lib/panoptimon/monitor.rb, line 84
def _init_collector (conf)
  name    = conf.delete(:name)
  command = conf.delete(:command)
  full_cmd = [command.to_s] + conf[:args].to_a
  logger.debug "#{name} command: #{full_cmd}"
  collector = Collector.new(
    name: name,
    bus: @bus,
    command: full_cmd,
    config: conf,
  )
  collectors << collector
end
_init_plugin(conf) click to toggle source
# File lib/panoptimon/monitor.rb, line 129
def _init_plugin (conf)
  name = conf.delete(:name)
  rb   = conf.delete(:rb)
  setup = eval("->(name, config, monitor) {#{rb.open.read}\n}",
    empty_binding, rb.to_s, 1)
  callback = begin; setup.call(name, conf, self)
    rescue; raise "error loading plugin '#{name}' - #{$!}"; end
  logger.debug "plugin #{callback} - #{plugins[name]}"
  plugins[name] = callback unless callback.nil?
  loaded_plugins[name] = conf.clone # XXX need a plugin object?
end
_load_collector_config(file) click to toggle source
# File lib/panoptimon/monitor.rb, line 49
def _load_collector_config (file)
  conf = JSON.parse(file.read, {:symbolize_names => true})

  # Determine the command path
  collector_name = file.basename.sub(/\.json$/, '').to_s
  command = Pathname.new(conf[:exec] || collector_name)
  command = file.dirname + collector_name + command \
    unless command.absolute?

  command = _autodetect_collector_command_path(collector_name) \
    unless command.exist? || !conf[:exec].nil?

  # TODO - interval/timeout defaults should be configurable
  return conf.
    merge({
      name: collector_name,
      interval: (self.config.collector_interval || 99).to_i,
      timeout:  (self.config.collector_timeout || 99).to_i
    }) {|k,a,b| a}.
    merge({command: command})
end
_load_plugin_config(file) click to toggle source
# File lib/panoptimon/monitor.rb, line 112
def _load_plugin_config (file)
  conf = JSON.parse(file.read, {:symbolize_names => true})
  base = file.basename.sub(/\.json$/, '').to_s

  # TODO support conf[:require] -> class.setup(conf) scheme?
  rb = conf[:require] || "#{base}.rb"
  rb = file.dirname + base + rb
  return conf.
    merge({
      name: base,
    }) {|k,a,b| a}.
    merge({
      base: base,
      rb: rb
    })
end
bus_driver(metric) click to toggle source
# File lib/panoptimon/monitor.rb, line 171
def bus_driver(metric)
  logger.debug {"metric: #{metric.inspect}"}
  metric.each {|k,v| @cached[k] = v} if @cached
  plugins.each {|n,p|
    begin p.call(metric)
    rescue => e
      logger.warn "plugin '#{n}' error: " +
        "#{e}\n  #{e.backtrace[0].sub(%r{^.*/?(plugins/)}, '\1')}"
      plugins.delete(n)
    end
  }
end
empty_binding() click to toggle source
# File lib/panoptimon/monitor.rb, line 107
def empty_binding; binding; end
enable_cache(arg=true) click to toggle source
# File lib/panoptimon/monitor.rb, line 167
def enable_cache(arg=true);
  if arg; @cached ||= {}; else; @cached = nil; end
end
find_collectors() click to toggle source
# File lib/panoptimon/monitor.rb, line 30
def find_collectors
  _dirjson(config.collectors_dir)
end
find_plugins() click to toggle source
# File lib/panoptimon/monitor.rb, line 34
def find_plugins
  _dirjson(config.plugins_dir)
end
http() click to toggle source
# File lib/panoptimon/monitor.rb, line 98
def http
  return @http unless @http.nil?
  # TODO rescue LoadError => nicer error message
  require 'panoptimon/http'
  @http = HTTP.new
  logger.warn "Serving http on #{@http.hostport}"
  @http
end
load_collectors() click to toggle source
# File lib/panoptimon/monitor.rb, line 38
def load_collectors
  find_collectors.each {|f|
    begin
      _init_collector(_load_collector_config(f))
    rescue => ex
      logger.error "collector #{f} failed to load: \n" +
        "  #{ex.message} \n  #{ex.backtrace[0]}"
    end
  }
end
load_plugins() click to toggle source
# File lib/panoptimon/monitor.rb, line 108
def load_plugins
  find_plugins.each {|f| _init_plugin(_load_plugin_config(f)) }
end
run() click to toggle source
# File lib/panoptimon/monitor.rb, line 141
def run

  runall = ->() {
    logger.debug "beep"
    collectors.each {|c|
      logger.info "#{c.cmd} (#{c.running? ? 'on ' : 'off'
        }) last run time: #{c.last_run_time}"
      next if c.last_run_time + c.interval > Time.now or c.running?
      c.run
    }
  }
  EM.next_tick(&runall)
  logger.warn 'no collectors' if collectors.length == 0
  minterval = collectors.map{|c| c.interval}.min
  minterval = 67 if minterval.nil? # XXX should never happen
  logger.debug "minimum: #{minterval}"
  EM.add_periodic_timer(minterval, &runall);

  @http.start if @http

end
stop() click to toggle source
# File lib/panoptimon/monitor.rb, line 163
def stop
  EM.stop
end