salus 0.1.2
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 +7 -0
- data/.gitignore +12 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Gemfile +6 -0
- data/LICENSE.md +29 -0
- data/README.md +254 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/salus +12 -0
- data/lib/salus.rb +183 -0
- data/lib/salus/cli.rb +53 -0
- data/lib/salus/cli/baseutils.rb +92 -0
- data/lib/salus/cli/zabbix.rb +150 -0
- data/lib/salus/configuration.rb +38 -0
- data/lib/salus/group.rb +159 -0
- data/lib/salus/logging.rb +15 -0
- data/lib/salus/metric.rb +173 -0
- data/lib/salus/metric/absolute.rb +21 -0
- data/lib/salus/metric/counter.rb +41 -0
- data/lib/salus/metric/derive.rb +36 -0
- data/lib/salus/metric/gauge.rb +5 -0
- data/lib/salus/metric/text.rb +9 -0
- data/lib/salus/renderer.rb +8 -0
- data/lib/salus/renderer/base.rb +50 -0
- data/lib/salus/renderer/block.rb +13 -0
- data/lib/salus/renderer/collectd.rb +21 -0
- data/lib/salus/renderer/graphite.rb +14 -0
- data/lib/salus/renderer/stdout.rb +18 -0
- data/lib/salus/renderer/zabbixbulk.rb +30 -0
- data/lib/salus/renderer/zabbixsender.rb +24 -0
- data/lib/salus/thread.rb +8 -0
- data/lib/salus/thread/cpu.rb +18 -0
- data/lib/salus/thread/future.rb +168 -0
- data/lib/salus/thread/latch.rb +28 -0
- data/lib/salus/thread/lockable.rb +56 -0
- data/lib/salus/thread/monotonictime.rb +15 -0
- data/lib/salus/thread/observable.rb +117 -0
- data/lib/salus/thread/pool.rb +482 -0
- data/lib/salus/version.rb +3 -0
- data/lib/salus/zabbix.rb +30 -0
- data/salus.gemspec +29 -0
- metadata +143 -0
data/lib/salus/cli.rb
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require "thor"
|
|
2
|
+
require "yaml"
|
|
3
|
+
require "salus/cli/baseutils"
|
|
4
|
+
require "salus/cli/zabbix"
|
|
5
|
+
|
|
6
|
+
module Salus
|
|
7
|
+
class CLI < Thor
|
|
8
|
+
include BaseCliUtils
|
|
9
|
+
include Thor::Actions
|
|
10
|
+
|
|
11
|
+
register Salus::ZabbixCli, :zabbix, "zabbix", "Zabbix specific actions"
|
|
12
|
+
|
|
13
|
+
desc "once", "Run check once"
|
|
14
|
+
method_option :file, aliases: "-f", :type => :array, desc: "File(s) with metrics' definition"
|
|
15
|
+
method_option :state, aliases: "-s", :type => :string, desc: "State file location"
|
|
16
|
+
method_option :debug, aliases: "-d", :type => :boolean, :default => false
|
|
17
|
+
method_option :renderer, aliases: "-r", :type => :array, desc: "Append predefined renderers"
|
|
18
|
+
def once
|
|
19
|
+
Salus.logger.level = options[:debug] ? Logger::DEBUG : Logger::WARN
|
|
20
|
+
load_files(get_files(options))
|
|
21
|
+
state_file = get_state_file(options)
|
|
22
|
+
load_state(state_file)
|
|
23
|
+
append_renderers(options)
|
|
24
|
+
Salus.tick
|
|
25
|
+
save_state(state_file)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
desc "loop", "Run check loop"
|
|
29
|
+
method_option :file, aliases: "-f", :type => :array, desc: "File(s) with metrics' definition"
|
|
30
|
+
method_option :debug, aliases: "-d", :type => :boolean, :default => false
|
|
31
|
+
method_option :renderer, aliases: "-r", :type => :array, desc: "Append predefined renderers"
|
|
32
|
+
def loop
|
|
33
|
+
Salus.logger.level = options[:debug] ? Logger::DEBUG : Logger::WARN
|
|
34
|
+
load_files(get_files(options))
|
|
35
|
+
append_renderers(options)
|
|
36
|
+
Salus.run
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
default_task :once
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def append_renderers(options={})
|
|
43
|
+
renderers = options.fetch(:renderer, Salus.renders.empty? ? ["stdout"] : [])
|
|
44
|
+
|
|
45
|
+
BaseRenderer.descendants.each do |m|
|
|
46
|
+
sym = m.name.split('::').last.downcase.sub(/renderer$/, '')
|
|
47
|
+
if renderers.include?(sym)
|
|
48
|
+
Salus.render(m.new)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
module Salus
|
|
2
|
+
module BaseCliUtils
|
|
3
|
+
include Logging
|
|
4
|
+
SALUS_STATE_FILE = "salus.state.yml"
|
|
5
|
+
SALUS_FILE = "Salusfile"
|
|
6
|
+
SALUS_GLOB = "*.salus"
|
|
7
|
+
|
|
8
|
+
def read_file(file)
|
|
9
|
+
ret = nil
|
|
10
|
+
File.open(file, "r") do |f|
|
|
11
|
+
f.flock(File::LOCK_SH)
|
|
12
|
+
ret = f.read
|
|
13
|
+
f.flock(File::LOCK_UN)
|
|
14
|
+
end
|
|
15
|
+
ret
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def write_file(file, data)
|
|
19
|
+
ret = nil
|
|
20
|
+
File.open(file, File::RDWR|File::CREAT) do |f|
|
|
21
|
+
f.flock(File::LOCK_EX)
|
|
22
|
+
begin
|
|
23
|
+
f.rewind
|
|
24
|
+
ret = f.write(data)
|
|
25
|
+
f.flush
|
|
26
|
+
f.truncate(f.pos)
|
|
27
|
+
ensure
|
|
28
|
+
f.flock(File::LOCK_UN)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
ret
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def load_files(files)
|
|
35
|
+
raise "No metric definition files found" if files.empty?
|
|
36
|
+
files.each do |file|
|
|
37
|
+
begin
|
|
38
|
+
Salus.load(file)
|
|
39
|
+
rescue Exception => e
|
|
40
|
+
log ERROR, "Failed to load #{file}: " + e.message
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def load_state(file)
|
|
46
|
+
return unless file
|
|
47
|
+
Salus.load_state do
|
|
48
|
+
begin
|
|
49
|
+
YAML.load(read_file(file)) if File.exists?(file)
|
|
50
|
+
rescue Exception => e
|
|
51
|
+
log ERROR, "Failed to load state #{file}: " + e.message
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def save_state(file)
|
|
57
|
+
return unless file
|
|
58
|
+
Salus.save_state do |data|
|
|
59
|
+
begin
|
|
60
|
+
write_file(file, data.to_yaml)
|
|
61
|
+
rescue Exception => e
|
|
62
|
+
log ERROR, "Failed to save state #{file}: " + e.message
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def get_state_file(options={})
|
|
68
|
+
options.fetch(:state,
|
|
69
|
+
Salus.var(:state_file,
|
|
70
|
+
File.join(Dir.pwd, SALUS_STATE_FILE)))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def get_files(options={})
|
|
74
|
+
if options.key?(:file)
|
|
75
|
+
ret = []
|
|
76
|
+
options[:file].each do |file|
|
|
77
|
+
next unless File.exists?(file)
|
|
78
|
+
if File.directory?(file)
|
|
79
|
+
ret += Dir.glob(File.join(file, SALUS_GLOB)).sort
|
|
80
|
+
else
|
|
81
|
+
ret.push(file)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
ret
|
|
85
|
+
elsif File.exists?(SALUS_FILE)
|
|
86
|
+
[SALUS_FILE]
|
|
87
|
+
else
|
|
88
|
+
Dir.glob(SALUS_GLOB).sort
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
require "salus/zabbix"
|
|
2
|
+
|
|
3
|
+
module Salus
|
|
4
|
+
class ZabbixCacheRenderer < BaseRenderer
|
|
5
|
+
ZABBIX_DEFAULT_TTL = 60
|
|
6
|
+
attr_reader :data
|
|
7
|
+
|
|
8
|
+
def render(data)
|
|
9
|
+
@data = {}
|
|
10
|
+
iterate(data) do |name, metric|
|
|
11
|
+
name = name.gsub(/\.\[/, '[')
|
|
12
|
+
value = metric.value
|
|
13
|
+
# Metric cache TTL is a half of real metric TTL
|
|
14
|
+
ttl = metric.ttl.nil? ? ZABBIX_DEFAULT_TTL : (metric.ttl / 2)
|
|
15
|
+
@data[name] = {timestamp: metric.timestamp, cache_ttl: ttl, value: value}
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class ZabbixCli < Thor
|
|
21
|
+
include BaseCliUtils
|
|
22
|
+
include Thor::Actions
|
|
23
|
+
ZABBIX_CACHE_FILE = "zabbix.cache.yml"
|
|
24
|
+
|
|
25
|
+
desc "discover NAME", "Run discovery"
|
|
26
|
+
method_option :file, aliases: "-f", :type => :array, desc: "File(s) with metrics' definition"
|
|
27
|
+
method_option :debug, aliases: "-d", :type => :boolean, :default => false
|
|
28
|
+
def discover(name)
|
|
29
|
+
Salus.logger.level = options[:debug] ? Logger::DEBUG : Logger::WARN
|
|
30
|
+
load_files(get_files(options))
|
|
31
|
+
puts Salus.discovery(name)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
desc "parameter NAME", "Get a requested parameter"
|
|
35
|
+
method_option :file, aliases: "-f", :type => :array, desc: "File(s) with metrics' definition"
|
|
36
|
+
method_option :state, aliases: "-s", :type => :string, desc: "State file location"
|
|
37
|
+
method_option :cache, aliases: "-c", :type => :string, desc: "Cache file location"
|
|
38
|
+
method_option :cache_ttl, aliases: "-t", :type => :numeric, desc: "Force metric cache ttl"
|
|
39
|
+
method_option :debug, aliases: "-d", :type => :boolean, :default => false
|
|
40
|
+
def parameter(name)
|
|
41
|
+
Salus.logger.level = options[:debug] ? Logger::DEBUG : Logger::WARN
|
|
42
|
+
load_files(get_files(options))
|
|
43
|
+
|
|
44
|
+
cache_file = options.fetch(:cache,
|
|
45
|
+
Salus.vars.fetch(:zabbix_cache_file,
|
|
46
|
+
File.join(Dir.pwd, ZABBIX_CACHE_FILE)))
|
|
47
|
+
cache = load_cache(cache_file)
|
|
48
|
+
|
|
49
|
+
if (cache.key?(name) && !expired?(cache[name], options))
|
|
50
|
+
STDOUT.puts cache[name][:value] unless cache[name][:value].nil?
|
|
51
|
+
return
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
state_file = get_state_file(options)
|
|
55
|
+
load_state(state_file)
|
|
56
|
+
|
|
57
|
+
render = ZabbixCacheRenderer.new
|
|
58
|
+
Salus.renders.clear
|
|
59
|
+
Salus.render(render)
|
|
60
|
+
Salus.tick
|
|
61
|
+
cache = render.data
|
|
62
|
+
|
|
63
|
+
if (cache.key?(name))
|
|
64
|
+
STDOUT.puts cache[name][:value] unless cache[name][:value].nil?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
save_state(state_file)
|
|
68
|
+
save_cache(cache_file, cache)
|
|
69
|
+
raise "Unknown parameter #{name}" unless cache.key?(name)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
desc "bulk GROUP", "Get a bunch of parameters under the GROUP group"
|
|
73
|
+
method_option :file, aliases: "-f", :type => :array, desc: "File(s) with metrics' definition"
|
|
74
|
+
method_option :state, aliases: "-s", :type => :string, desc: "State file location"
|
|
75
|
+
method_option :debug, aliases: "-d", :type => :boolean, :default => false
|
|
76
|
+
def bulk(group=nil)
|
|
77
|
+
Salus.logger.level = options[:debug] ? Logger::DEBUG : Logger::WARN
|
|
78
|
+
load_files(get_files(options))
|
|
79
|
+
|
|
80
|
+
cache_file = options.fetch(:cache,
|
|
81
|
+
Salus.var(:zabbix_cache_file,
|
|
82
|
+
File.join(Dir.pwd, ZABBIX_CACHE_FILE)))
|
|
83
|
+
cache = load_cache(cache_file)
|
|
84
|
+
|
|
85
|
+
re = group.nil? ? // : /^#{Regexp.escape(group)}\./
|
|
86
|
+
keys = cache.keys.grep(re)
|
|
87
|
+
if !keys.empty? && (keys.reduce(true) { |x, v| x &= !expired?(cache[v], options) })
|
|
88
|
+
result = {}
|
|
89
|
+
keys.each do |key|
|
|
90
|
+
name = key.sub(re, '')
|
|
91
|
+
name = name.gsub(/\.\[/, '[')
|
|
92
|
+
|
|
93
|
+
parts = name.split(/\./)
|
|
94
|
+
node = result
|
|
95
|
+
parts[0...-1].each do |part|
|
|
96
|
+
node[part] = {} unless node.key?(part)
|
|
97
|
+
node = node[part]
|
|
98
|
+
end
|
|
99
|
+
node[parts.last] = cache[key][:value]
|
|
100
|
+
end
|
|
101
|
+
STDOUT.puts result.to_json
|
|
102
|
+
return
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
state_file = get_state_file(options)
|
|
106
|
+
load_state(state_file)
|
|
107
|
+
|
|
108
|
+
Salus.renders.clear
|
|
109
|
+
Salus.render(ZabbixBulkRenderer.new(group: group))
|
|
110
|
+
render = ZabbixCacheRenderer.new
|
|
111
|
+
Salus.render(render)
|
|
112
|
+
Salus.tick
|
|
113
|
+
cache = render.data
|
|
114
|
+
|
|
115
|
+
save_state(state_file)
|
|
116
|
+
save_cache(cache_file, cache)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
private
|
|
120
|
+
def expired?(metric, options={})
|
|
121
|
+
return true if metric.nil?
|
|
122
|
+
ttl = options.fetch(:cache_ttl, metric[:cache_ttl])
|
|
123
|
+
ttl ||= 0
|
|
124
|
+
(Time.now.to_f > metric[:timestamp] + ttl)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def load_cache(file)
|
|
128
|
+
return {} unless file
|
|
129
|
+
begin
|
|
130
|
+
if File.exists?(file)
|
|
131
|
+
YAML.load(read_file(file))
|
|
132
|
+
else
|
|
133
|
+
{}
|
|
134
|
+
end
|
|
135
|
+
rescue Exception => e
|
|
136
|
+
log ERROR, "Failed to load state #{file}: " + e.message
|
|
137
|
+
{}
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def save_cache(file, data)
|
|
142
|
+
return unless file
|
|
143
|
+
begin
|
|
144
|
+
write_file(file, data.to_yaml)
|
|
145
|
+
rescue Exception => e
|
|
146
|
+
log ERROR, "Failed to save state #{file}: " + e.message
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Salus
|
|
2
|
+
module Configuration
|
|
3
|
+
# An array of valid keys in the options hash when configuring Salus.
|
|
4
|
+
VALID_OPTIONS_KEYS = %i(min_threads max_threads interval tick_timeout render_timeout logger).freeze
|
|
5
|
+
|
|
6
|
+
# @private
|
|
7
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
|
8
|
+
|
|
9
|
+
# Sets all configuration options to their default values
|
|
10
|
+
# when this module is extended.
|
|
11
|
+
def self.extended(base)
|
|
12
|
+
base.reset
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Convenience method to allow configuration options to be set in a block.
|
|
16
|
+
def configure
|
|
17
|
+
yield self
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
# Creates a hash of options and their values.
|
|
21
|
+
def options
|
|
22
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
|
23
|
+
option.merge!(key => send(key))
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Resets all configuration options to the defaults.
|
|
28
|
+
def reset
|
|
29
|
+
self.min_threads = CPU.count / 2
|
|
30
|
+
self.min_threads = 1 if self.min_threads == 0
|
|
31
|
+
self.max_threads = CPU.count * 2
|
|
32
|
+
self.interval = 30
|
|
33
|
+
self.tick_timeout = 15
|
|
34
|
+
self.render_timeout = 10
|
|
35
|
+
self.logger = Logger.new(STDERR)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
data/lib/salus/group.rb
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require "forwardable"
|
|
2
|
+
require "salus/metric"
|
|
3
|
+
require "salus/metric/absolute"
|
|
4
|
+
require "salus/metric/counter"
|
|
5
|
+
require "salus/metric/derive"
|
|
6
|
+
require "salus/metric/gauge"
|
|
7
|
+
require "salus/metric/text"
|
|
8
|
+
|
|
9
|
+
module Salus
|
|
10
|
+
class Group
|
|
11
|
+
extend Forwardable
|
|
12
|
+
include Lockable
|
|
13
|
+
|
|
14
|
+
def_delegators :@_metrics, :[], :key?, :values_at, :fetch, :length, :delete, :empty?
|
|
15
|
+
|
|
16
|
+
def initialize(defaults={}, &block)
|
|
17
|
+
@_metrics = {}
|
|
18
|
+
@_groups = {}
|
|
19
|
+
@_cache = {}
|
|
20
|
+
@_proc = block
|
|
21
|
+
@_opts = defaults.clone
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Metric.descendants.each do |m|
|
|
25
|
+
sym = m.name.split('::').last.downcase.to_sym
|
|
26
|
+
define_method(sym) do |title, args={}, &blk|
|
|
27
|
+
raise ArgumentError, "Metric needs a name!" if title.nil? or !title.is_a?(String)
|
|
28
|
+
|
|
29
|
+
unless @_metrics.key?(title)
|
|
30
|
+
@_metrics[title] = m.new(@_opts)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
@_metrics[title].push(args, &blk)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def on_win?
|
|
38
|
+
Salus.on_win?
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def var(arg, default=nil, &block)
|
|
42
|
+
Salus.var(arg, default, &block)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def default(opts)
|
|
46
|
+
return unless opts.is_a?(Hash)
|
|
47
|
+
opts.each do |k, v|
|
|
48
|
+
next if [:value, :timestamp].include?(k)
|
|
49
|
+
@_opts[k] = v
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def group(title, &block)
|
|
54
|
+
synchronize do
|
|
55
|
+
unless @_groups.key?(title)
|
|
56
|
+
@_groups[title] = Group.new(@_opts, &block)
|
|
57
|
+
if @_cache.key?(title)
|
|
58
|
+
@_groups[title].load(@_cache[title])
|
|
59
|
+
@_cache.delete(title)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def groups
|
|
66
|
+
synchronize { @_groups }
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def has_subgroups?
|
|
70
|
+
synchronize { !@_groups.empty? }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def value(title)
|
|
74
|
+
synchronize { @_metrics.key?(title) ? @_metrics[title].value : nil }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def keys(allow_mute=false)
|
|
78
|
+
synchronize do
|
|
79
|
+
if allow_mute
|
|
80
|
+
@_metrics.keys
|
|
81
|
+
else
|
|
82
|
+
@_metrics.keys.select { |x| !@_metrics[x].mute? }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def values(allow_mute=false)
|
|
88
|
+
synchronize do
|
|
89
|
+
if allow_mute
|
|
90
|
+
@_metrics.values
|
|
91
|
+
else
|
|
92
|
+
@_metrics.values.select { |x| !x.mute? }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def each(allow_mute=false, &block)
|
|
98
|
+
synchronize do
|
|
99
|
+
if allow_mute
|
|
100
|
+
@_metrics.each(&block)
|
|
101
|
+
else
|
|
102
|
+
@_metrics.select { |k, v| !v.mute? }.each(&block)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def load(data)
|
|
108
|
+
return unless data
|
|
109
|
+
return if data.empty?
|
|
110
|
+
synchronize do
|
|
111
|
+
if data.key?(:defaults)
|
|
112
|
+
@_opts = data[:defaults].clone
|
|
113
|
+
end
|
|
114
|
+
if data.key?(:metrics)
|
|
115
|
+
types = Metric.descendants.map{ |x| x.name.split("::").last }
|
|
116
|
+
data[:metrics].each do |k, v|
|
|
117
|
+
next unless v.key?(:type)
|
|
118
|
+
next unless types.include?(v[:type])
|
|
119
|
+
@_metrics[k] = Object.const_get("Salus::" + v[:type]).new(@_opts)
|
|
120
|
+
@_metrics[k].load(v)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
if data.key?(:groups)
|
|
124
|
+
@_cache = data[:groups]
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def save
|
|
130
|
+
to_h
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def to_h
|
|
134
|
+
ret = {}
|
|
135
|
+
synchronize do
|
|
136
|
+
unless @_metrics.empty?
|
|
137
|
+
ret[:metrics] = {}
|
|
138
|
+
@_metrics.each { |k, v| ret[:metrics][k] = v.to_h }
|
|
139
|
+
end
|
|
140
|
+
unless @_groups.empty?
|
|
141
|
+
ret[:groups] = {}
|
|
142
|
+
@_groups.each { |k, v| ret[:groups][k] = v.to_h }
|
|
143
|
+
end
|
|
144
|
+
unless @_opts.empty?
|
|
145
|
+
ret[:defaults] = @_opts
|
|
146
|
+
end
|
|
147
|
+
ret
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def tick
|
|
152
|
+
instance_eval(&@_proc)
|
|
153
|
+
@_groups.each do |k, v|
|
|
154
|
+
v.tick
|
|
155
|
+
end
|
|
156
|
+
@_cache.clear unless @_cache.empty?
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|