newrelic_zfs 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/newrelic_zfs +16 -21
- data/lib/collector.rb +90 -0
- data/lib/newrelic_zfs/converter.rb +41 -0
- data/lib/newrelic_zfs/metric.rb +59 -0
- data/lib/newrelic_zfs/processor.rb +43 -0
- metadata +7 -3
- data/lib/newrelic_zfs.rb +0 -116
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1a84c6a5a80661fbb31b730dbb65a8156af454d3
|
4
|
+
data.tar.gz: 7bc403399ec882917003132f15c3461c56b15f69
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f77a39debb534c889fcc354fd0a3dffa875cf3b1618b51988aa0c099c1af8e7640b87c5dad0490513612bcc5c0e488261944f1bbb0b1b5eb1bd8307c8177defa
|
7
|
+
data.tar.gz: 86c29b3d2724b6c69efd99a707a6a9c7bac7095b2c64a45b33e9e52fb3338b2116d802bf5da71e1bba0a77277e393acda5413e249bf85f2644cc88ebecbd57cb
|
data/bin/newrelic_zfs
CHANGED
@@ -6,7 +6,7 @@ require 'bundler/setup'
|
|
6
6
|
require 'newrelic_plugin'
|
7
7
|
require 'trollop'
|
8
8
|
|
9
|
-
require '
|
9
|
+
require 'collector'
|
10
10
|
require 'newrelic_zfs/config'
|
11
11
|
|
12
12
|
module ZfsAgent
|
@@ -18,31 +18,28 @@ module ZfsAgent
|
|
18
18
|
agent_config_options :instance_name, :print_metrics
|
19
19
|
agent_human_labels("ZFS Agent") { instance_name }
|
20
20
|
|
21
|
+
def initialize(context, options)
|
22
|
+
puts 'Initializing ZfsAgent'
|
23
|
+
@collector = Collector.new
|
24
|
+
super(context, options)
|
25
|
+
end
|
26
|
+
|
21
27
|
def poll_cycle
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
stat_name = 'ZPools/' + stat.name + '/' + stat.pool_name
|
31
|
-
if print_metrics
|
32
|
-
puts 'Reporting metric named ' + stat_name + ' with value ' + stat.value.to_s + ' and unit ' + stat.unit
|
33
|
-
end
|
34
|
-
report_metric stat_name, stat.unit, stat.value
|
28
|
+
puts 'Starting metrics collection'
|
29
|
+
metrics = @collector.collect_stats
|
30
|
+
puts "#{metrics.length} metrics collected, sending to New Relic"
|
31
|
+
metrics.each do |metric|
|
32
|
+
puts 'Reporting metric named ' + metric.newrelic_name + ' with value ' +
|
33
|
+
metric.value.to_s + ' and unit ' + metric.unit
|
35
34
|
end
|
36
35
|
|
37
|
-
|
38
|
-
|
39
|
-
puts 'Reporting metric named ' + stat_name + ' with value ' + health_total.to_s + ' and unit Value'
|
36
|
+
metrics.each do |metric|
|
37
|
+
report_metric metric.newrelic_name, metric.unit, metric.value
|
40
38
|
end
|
41
|
-
|
39
|
+
puts 'Finished sending metrics to New Relic'
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
45
|
-
|
46
43
|
opts = Trollop::options do
|
47
44
|
opt :config_file, "Config file", :type => :string
|
48
45
|
end
|
@@ -55,6 +52,4 @@ module ZfsAgent
|
|
55
52
|
NewRelic::Plugin::Config::config_file = config_file_path
|
56
53
|
NewRelic::Plugin::Setup.install_agent :zfs, ZfsAgent
|
57
54
|
NewRelic::Plugin::Run.setup_and_run
|
58
|
-
|
59
|
-
puts 'Newrelic ZFS Agent started'
|
60
55
|
end
|
data/lib/collector.rb
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
require_relative 'newrelic_zfs/metric'
|
2
|
+
require_relative 'newrelic_zfs/processor'
|
3
|
+
require_relative 'newrelic_zfs/converter'
|
4
|
+
|
5
|
+
class Collector
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@blacklist = ['Altroot']
|
9
|
+
@summary_metrics = { 'UnhealthyCount' => Metric.method(:sum), 'Capacity' => Metric.method(:mean),
|
10
|
+
'Size' => Metric.method(:sum) }
|
11
|
+
@processor = Processor.new
|
12
|
+
@converter = Converter.new
|
13
|
+
end
|
14
|
+
def collect_stats
|
15
|
+
metrics = []
|
16
|
+
first = true
|
17
|
+
|
18
|
+
run_command.each_line do |line|
|
19
|
+
if first
|
20
|
+
puts 'Processing as a header line'
|
21
|
+
puts line
|
22
|
+
@processor.process_header_line(line)
|
23
|
+
first = false
|
24
|
+
else
|
25
|
+
puts 'Processing as a metric line'
|
26
|
+
puts line
|
27
|
+
metrics = metrics + @processor.line_to_metrics(line)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
puts 'Filtering out blacklist metrics'
|
32
|
+
metrics = metrics.find_all { |metric|
|
33
|
+
!@blacklist.include?(metric.name)
|
34
|
+
}
|
35
|
+
|
36
|
+
#Convert the value first, otherwise adding won't work when calculating summary metrics
|
37
|
+
puts 'Starting to convert metrics'
|
38
|
+
metrics = convert_metrics(metrics)
|
39
|
+
puts 'Finished converting metrics'
|
40
|
+
puts 'Starting to calculate and convert summary metrics'
|
41
|
+
metrics = metrics + convert_metrics(get_summary_metrics(metrics))
|
42
|
+
puts 'Finished calculating and converting summary metrics'
|
43
|
+
|
44
|
+
metrics
|
45
|
+
end
|
46
|
+
|
47
|
+
def get_summary_metrics(metrics)
|
48
|
+
puts 'Started to calculate summary metrics'
|
49
|
+
metrics_hash = {}
|
50
|
+
metrics.each do |metric|
|
51
|
+
puts "Processing metric #{metric.name}"
|
52
|
+
if @summary_metrics.has_key?(metric.name)
|
53
|
+
puts 'Metric is in summmary metrics list, processing it'
|
54
|
+
summary_metric = metrics_hash[metric.name]
|
55
|
+
if summary_metric.nil?
|
56
|
+
puts 'No previous summary metric existed, creating a new one'
|
57
|
+
summary_metric = Metric.new
|
58
|
+
summary_metric.name = metric.name
|
59
|
+
summary_metric.unit = metric.unit
|
60
|
+
summary_metric.aggregate_function = @summary_metrics[metric.name]
|
61
|
+
metrics_hash[metric.name] = summary_metric
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "Adding value #{metric.value} to summary metric #{summary_metric.name}"
|
65
|
+
summary_metric.add_value(metric.value)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
puts 'Finished calculating summary metrics'
|
70
|
+
metrics_hash.values
|
71
|
+
end
|
72
|
+
|
73
|
+
def convert_metrics(metrics)
|
74
|
+
metrics.each do |metric|
|
75
|
+
puts "Converting metric name: #{metric.name}"
|
76
|
+
metric.name = @converter.convert_name(metric)
|
77
|
+
puts "Metric name is now: #{metric.name}"
|
78
|
+
puts "Converting metric value #{metric.value}"
|
79
|
+
metric.set_value @converter.convert_value(metric)
|
80
|
+
puts "Value is now: #{metric.value}"
|
81
|
+
puts "Converting metric unit: #{metric.unit}"
|
82
|
+
metric.unit = @converter.convert_unit(metric)
|
83
|
+
puts "Unit is now: #{metric.unit}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def run_command
|
88
|
+
`zpool list`
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class Converter
|
2
|
+
def initialize
|
3
|
+
@name_conversions = { :health => 'UnhealthyCount', :cap => 'Capacity'}
|
4
|
+
@value_conversions = { :online => 0, :- => 0}
|
5
|
+
@unit_conversions = { :b => 'bytes', :k => 'kilobytes', :m => 'megabytes',
|
6
|
+
:g => 'gigabytes', :t => 'terabytes', :p => 'petabytes'}
|
7
|
+
@last_seen_unit = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def convert_name(metric)
|
11
|
+
_do_convert(metric.name, @name_conversions)
|
12
|
+
end
|
13
|
+
|
14
|
+
def convert_value(metric)
|
15
|
+
converted = _do_convert(metric.raw_value, @value_conversions)
|
16
|
+
Float(converted) rescue 1
|
17
|
+
end
|
18
|
+
|
19
|
+
def convert_unit(metric)
|
20
|
+
converted = _do_convert(metric.unit, @unit_conversions)
|
21
|
+
|
22
|
+
if converted.nil?
|
23
|
+
converted = @last_seen_unit[metric.name]
|
24
|
+
end
|
25
|
+
|
26
|
+
if converted.nil?
|
27
|
+
converted = 'Value'
|
28
|
+
end
|
29
|
+
|
30
|
+
@last_seen_unit[metric.name] = converted
|
31
|
+
|
32
|
+
converted
|
33
|
+
end
|
34
|
+
|
35
|
+
def _do_convert(value, conversion_hash)
|
36
|
+
symbol = value.nil? ? nil : value.downcase.to_sym rescue value
|
37
|
+
converted = conversion_hash[symbol]
|
38
|
+
|
39
|
+
converted.nil? ? value : converted
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
class Metric
|
2
|
+
attr_accessor :pool_name
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :unit
|
5
|
+
attr_reader :count
|
6
|
+
attr_writer :aggregate_function
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@count = 0
|
10
|
+
@value = 0
|
11
|
+
end
|
12
|
+
|
13
|
+
def is_summary_metric?
|
14
|
+
!@aggregate_function.nil?
|
15
|
+
end
|
16
|
+
|
17
|
+
def add_value(value)
|
18
|
+
if @aggregate_function.nil?
|
19
|
+
@value = value
|
20
|
+
else
|
21
|
+
@value = @value + value rescue value
|
22
|
+
@count = @count + 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def set_value(value)
|
27
|
+
@value = value
|
28
|
+
end
|
29
|
+
|
30
|
+
def value
|
31
|
+
@aggregate_function.nil? ? @value : @aggregate_function.call(@value, @count)
|
32
|
+
end
|
33
|
+
|
34
|
+
def raw_value
|
35
|
+
@value
|
36
|
+
end
|
37
|
+
|
38
|
+
def newrelic_name
|
39
|
+
if is_summary_metric?
|
40
|
+
"ZPools/#{@name}"
|
41
|
+
else
|
42
|
+
"ZPools/#{@name}/#{@pool_name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.mean(value, count)
|
47
|
+
if value.nil? || count.nil?
|
48
|
+
0
|
49
|
+
elsif count == 0
|
50
|
+
value
|
51
|
+
else
|
52
|
+
value.to_f / count
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.sum(value, count)
|
57
|
+
value
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require_relative 'metric'
|
2
|
+
|
3
|
+
class Processor
|
4
|
+
def process_header_line(line)
|
5
|
+
@headers = []
|
6
|
+
line.split(' ').each_with_index do |header, index|
|
7
|
+
@headers[index] = header.capitalize
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def line_to_metrics(line)
|
12
|
+
pool_name = nil
|
13
|
+
metrics = []
|
14
|
+
line.split(' ').each_with_index do |value, index|
|
15
|
+
if @headers[index].casecmp('Name') == 0
|
16
|
+
pool_name = value
|
17
|
+
else
|
18
|
+
metric = Metric.new
|
19
|
+
split = split_value_and_unit(value)
|
20
|
+
metric.name = @headers[index]
|
21
|
+
metric.add_value split[0]
|
22
|
+
metric.unit = split[1]
|
23
|
+
metrics << metric
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
metrics.each { |metric|
|
28
|
+
metric.pool_name = pool_name
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def split_value_and_unit(text)
|
33
|
+
match = /\d+\.?\d*/.match(text)
|
34
|
+
#If numeric value
|
35
|
+
if match
|
36
|
+
value = match[0]
|
37
|
+
unit = text.slice(value.length, text.length)
|
38
|
+
[value, unit]
|
39
|
+
else
|
40
|
+
[text, nil]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: newrelic_zfs
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Reay
|
@@ -60,15 +60,19 @@ extensions: []
|
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
62
|
- bin/newrelic_zfs
|
63
|
-
- lib/
|
63
|
+
- lib/collector.rb
|
64
64
|
- lib/newrelic_zfs/config.rb
|
65
|
-
|
65
|
+
- lib/newrelic_zfs/converter.rb
|
66
|
+
- lib/newrelic_zfs/metric.rb
|
67
|
+
- lib/newrelic_zfs/processor.rb
|
68
|
+
homepage: https://github.com/tomreay/newrelic_zfs_plugin
|
66
69
|
licenses:
|
67
70
|
- MIT
|
68
71
|
metadata: {}
|
69
72
|
post_install_message:
|
70
73
|
rdoc_options: []
|
71
74
|
require_paths:
|
75
|
+
- lib/newrelic_zfs
|
72
76
|
- lib
|
73
77
|
required_ruby_version: !ruby/object:Gem::Requirement
|
74
78
|
requirements:
|
data/lib/newrelic_zfs.rb
DELETED
@@ -1,116 +0,0 @@
|
|
1
|
-
module ZFSTools
|
2
|
-
class Stat
|
3
|
-
attr_reader :pool_name
|
4
|
-
attr_reader :name
|
5
|
-
attr_reader :unit
|
6
|
-
attr_reader :value
|
7
|
-
|
8
|
-
def initialize(pool_name, name, unit, value)
|
9
|
-
@pool_name = pool_name
|
10
|
-
@name = name
|
11
|
-
@unit = unit
|
12
|
-
@value = value
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
class Pool
|
17
|
-
BLACK_LIST = ['Altroot']
|
18
|
-
|
19
|
-
attr_accessor :name
|
20
|
-
attr_reader :values
|
21
|
-
|
22
|
-
def initialize()
|
23
|
-
@values = {}
|
24
|
-
end
|
25
|
-
|
26
|
-
def add_value(heading, value)
|
27
|
-
if !BLACK_LIST.include?(heading)
|
28
|
-
@values.merge!({ heading => value })
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Converter
|
34
|
-
UNITS = { 'B' => 'bytes', 'K' => 'kilobytes', 'M' => 'megabytes', 'G' => 'gigabytes', 'T' => 'terabytes', 'P' => 'petabytes'}
|
35
|
-
HEADINGS = { 'Health' => 'UnhealthyCount' }
|
36
|
-
|
37
|
-
def convert(heading, value)
|
38
|
-
values = _split(value)
|
39
|
-
return [ _convert(heading, HEADINGS), _convert_d(values[1], UNITS, 'Value'), values[0] ]
|
40
|
-
end
|
41
|
-
|
42
|
-
def _split(value)
|
43
|
-
match = /\d+\.?\d*/.match(value)
|
44
|
-
#If numeric value
|
45
|
-
if match
|
46
|
-
numeric_part = match[0]
|
47
|
-
unit = value.slice(numeric_part.length, value.length)
|
48
|
-
return [numeric_part, unit]
|
49
|
-
elsif value == 'ONLINE'
|
50
|
-
return [0, '']
|
51
|
-
else
|
52
|
-
return [1, '']
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
def _convert_d(value, hash, default)
|
57
|
-
converted = _convert(value, hash)
|
58
|
-
|
59
|
-
if converted.nil?
|
60
|
-
converted = default
|
61
|
-
end
|
62
|
-
|
63
|
-
return converted
|
64
|
-
end
|
65
|
-
|
66
|
-
def _convert(value, hash)
|
67
|
-
converted = hash[value]
|
68
|
-
|
69
|
-
#If the converted value is null, use the original unit
|
70
|
-
if converted.nil?
|
71
|
-
converted = value
|
72
|
-
end
|
73
|
-
|
74
|
-
return converted
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class Collector
|
79
|
-
CONVERTER = Converter.new
|
80
|
-
|
81
|
-
def collect_stats()
|
82
|
-
first = true
|
83
|
-
headings = {}
|
84
|
-
pools = []
|
85
|
-
|
86
|
-
`zpool list`.each_line do |line|
|
87
|
-
#File.open('/Users/tomreay/Documents/workspaceWebApps/newrelic-zfs/list.txt').each_line do |line|
|
88
|
-
pool = Pool.new
|
89
|
-
line.split(' ').each_with_index do |token, index|
|
90
|
-
if first
|
91
|
-
headings.merge!({ index => token.capitalize})
|
92
|
-
else
|
93
|
-
if headings[index] == 'Name'
|
94
|
-
pool.name = token
|
95
|
-
else
|
96
|
-
pool.add_value(headings[index], token)
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
first = false
|
101
|
-
pools << pool
|
102
|
-
end
|
103
|
-
|
104
|
-
stats = []
|
105
|
-
pools.each do |pool|
|
106
|
-
pool.values.each do |heading, value|
|
107
|
-
converted = CONVERTER.convert(heading, value)
|
108
|
-
stat = Stat.new(pool.name, converted[0], converted[1], converted[2])
|
109
|
-
stats << stat
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
return stats
|
114
|
-
end
|
115
|
-
end
|
116
|
-
end
|