Class: Panoptimon::Monitor

Inherits:
Object
  • Object
show all
Includes:
Logger
Defined in:
lib/panoptimon/monitor.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logger

#logger, logger

Constructor Details

#initialize(args = {}) ⇒ Monitor

Returns a new instance of Monitor.



11
12
13
14
15
16
17
18
19
20
21
# 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

Instance Attribute Details

#busObject (readonly)

Returns the value of attribute bus.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def bus
  @bus
end

#cachedObject (readonly)

Returns the value of attribute cached.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def cached
  @cached
end

#collectorsObject (readonly)

Returns the value of attribute collectors.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def collectors
  @collectors
end

#configObject (readonly)

Returns the value of attribute config.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def config
  @config
end

#loaded_pluginsObject (readonly)

Returns the value of attribute loaded_plugins.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def loaded_plugins
  @loaded_plugins
end

#owdObject (readonly)

Returns the value of attribute owd.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def owd
  @owd
end

#pluginsObject (readonly)

Returns the value of attribute plugins.



8
9
10
# File 'lib/panoptimon/monitor.rb', line 8

def plugins
  @plugins
end

Instance Method Details

#_autodetect_collector_command_path(name) ⇒ Object

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



73
74
75
76
77
78
79
80
81
82
# 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) ⇒ Object

Search directories for JSON files



24
25
26
27
28
# 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) ⇒ Object



84
85
86
87
88
89
90
91
92
93
94
95
96
# 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) ⇒ Object



129
130
131
132
133
134
135
136
137
138
139
# 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) ⇒ Object



49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# 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) ⇒ Object



112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# 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) ⇒ Object



171
172
173
174
175
176
177
178
179
180
181
182
# 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_bindingObject



107
# File 'lib/panoptimon/monitor.rb', line 107

def empty_binding; binding; end

#enable_cache(arg = true) ⇒ Object



167
168
169
# File 'lib/panoptimon/monitor.rb', line 167

def enable_cache(arg=true);
  if arg; @cached ||= {}; else; @cached = nil; end
end

#find_collectorsObject



30
31
32
# File 'lib/panoptimon/monitor.rb', line 30

def find_collectors
  _dirjson(config.collectors_dir)
end

#find_pluginsObject



34
35
36
# File 'lib/panoptimon/monitor.rb', line 34

def find_plugins
  _dirjson(config.plugins_dir)
end

#httpObject



98
99
100
101
102
103
104
105
# 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_collectorsObject



38
39
40
41
42
43
44
45
46
47
# 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_pluginsObject



108
109
110
# File 'lib/panoptimon/monitor.rb', line 108

def load_plugins
  find_plugins.each {|f| _init_plugin(_load_plugin_config(f)) }
end

#runObject



141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# 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

#stopObject



163
164
165
# File 'lib/panoptimon/monitor.rb', line 163

def stop
  EM.stop
end