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 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