newrelic_zfs 1.0.0 → 1.0.1
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 +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
|