metrics-iostat-extended.rb
3.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#! /usr/bin/env ruby
# encoding: UTF-8
#
# metrics-iostat-extended
#
# DESCRIPTION:
# This plugin collects iostat data for a specified disk or all disks.
# Output is in Graphite format. See `man iostat` for detailed
# explaination of each field.
#
# OUTPUT:
# metric data
#
# PLATFORMS:
# Linux
#
# DEPENDENCIES:
# gem: sensu-plugin
# gem: socket
#
# USAGE:
# Collect metrics for all disks
# metrics-iostat-extended.rb
#
# Collect metrics for /dev/sda for 3 seconds
# metrics-iostat-extended.rb -d /dev/sda -i 3
#
# NOTES:
# The iostat command must be installed. On Debian/Redhat systems
# iostat is part of the sysstat package.
#
# LICENSE:
# Peter Fern <ruby@0xc0dedbad.com>
# Released under the same terms as Sensu (the MIT license); see LICENSE
# for details.
#
require 'sensu-plugin/metric/cli'
require 'open3'
require 'socket'
class IOStatExtended < Sensu::Plugin::Metric::CLI::Graphite
option :scheme,
description: 'Metric naming scheme, text to prepend to .$parent.$child',
long: '--scheme SCHEME',
default: "#{Socket.gethostname}.iostat"
option :disk,
description: 'Disk to gather stats for',
short: '-d DISK',
long: '--disk DISK',
required: false
option :excludedisk,
description: 'List of disks to exclude',
short: '-x DISK[,DISK]',
long: '--exclude-disk',
proc: proc { |a| a.split(',') }
option :interval,
description: 'Period over which statistics are calculated (in seconds)',
short: '-i SECONDS',
long: '--interval SECONDS',
default: 1
option :mappernames,
description: 'Display the registered device mapper names for any device mapper devices. Useful for viewing LVM2 statistics',
short: '-N',
long: '--show-dm-names',
boolean: true
def parse_results(raw)
stats = {}
key = nil
headers = nil
stage = :initial
raw.each_line do |line|
line.chomp!
next if line.empty?
case line
when /^(avg-cpu):/
stage = :cpu
key = Regexp.last_match[1]
headers = line.gsub(/%/, 'pct_').split(/\s+/)
headers.shift
next
when /^(Device):/
stage = :device
headers = line.gsub(/%/, 'pct_').split(/\s+/).map { |h| h.gsub(/\//, '_per_') }
headers.shift
next
end
next if stage == :initial
fields = line.split(/\s+/)
key = fields.shift if stage == :device
fields.shift if stage == :cpu
stats[key] = Hash[headers.zip(fields.map(&:to_f))]
end
stats
end
def run
cmd = 'iostat'
args = ['-x', config[:interval].to_s, '2']
args.push('-N') if config[:mappernames]
args.push(File.basename(config[:disk])) if config[:disk]
exclude_disk = if config[:excludedisk]
config[:excludedisk].map { |d| File.basename(d) }
else
[]
end
stdin, stdout, stderr = Open3.popen3(cmd, *args, unsetenv_others: true)
stdin.close
stderr.close
stats = parse_results(stdout.gets(nil))
stdout.close
timestamp = Time.now.to_i
stats.each do |disk, metrics|
next if exclude_disk.include? disk
metrics.each do |metric, value|
output [config[:scheme], disk, metric].join('.'), value, timestamp
end
end
ok
end
end