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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9fdd5091dd94e9ea8d6b2b9ac4a6f856b6a8f8b9
4
- data.tar.gz: 4be49358deb4349c9ce20f31cecda0bf79d1e6bf
3
+ metadata.gz: 1a84c6a5a80661fbb31b730dbb65a8156af454d3
4
+ data.tar.gz: 7bc403399ec882917003132f15c3461c56b15f69
5
5
  SHA512:
6
- metadata.gz: 4726fd516e06b68227dbcce1af4cd46c9afa61a3a6dd3f910dd5aadc5673291d587ac221d011481792230ddd7a53b5642236f2acb19b8c573b3eb01d70925b00
7
- data.tar.gz: cd7ba440866b75b6fb57ac14a0188b6eb8b94826b9c385baa5f2be0da4b5f9d7cbf3edaea4bdb0ba79825a7c101ed7127e68779d0916ddf7aa5b9778fdc604d5
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 'newrelic_zfs'
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
- collector = ZFSTools::Collector.new
23
- stats = collector.collect_stats
24
- health_total = 0
25
- stats.each do |stat|
26
- if stat.name == 'UnhealthyCount'
27
- health_total = health_total + stat.value
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
- stat_name = 'ZPools/UnhealthyCount'
38
- if print_metrics
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
- report_metric stat_name, 'Value', health_total
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.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/newrelic_zfs.rb
63
+ - lib/collector.rb
64
64
  - lib/newrelic_zfs/config.rb
65
- homepage: http://rubygems.org/gems/newrelic-zfs
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