diskmon 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/bin/diskmon +16 -0
- data/diskmon.gemspec +20 -0
- data/lib/diskmon.rb +73 -0
- data/lib/diskmon/client/collector.rb +24 -0
- data/lib/diskmon/client/config.rb +19 -0
- data/lib/diskmon/client/harddisk.rb +43 -0
- data/lib/diskmon/client/raidcontroller.rb +229 -0
- data/lib/diskmon/client/solarismapdev.rb +31 -0
- data/lib/diskmon/client/zpoolstree.rb +82 -0
- data/lib/diskmon/server/harddisk.rb +19 -0
- data/lib/diskmon/server/harddiskreport.rb +50 -0
- data/lib/diskmon/version.rb +3 -0
- metadata +110 -0
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Morion Black
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Diskmon
|
2
|
+
|
3
|
+
TODO: Write a gem description
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'diskmon'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install diskmon
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/bin/diskmon
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "rubygems"
|
3
|
+
require "diskmon"
|
4
|
+
require "optparse"
|
5
|
+
|
6
|
+
OptionParser.new do |opt|
|
7
|
+
opt.separator ""
|
8
|
+
opt.separator "Options:"
|
9
|
+
opt.separator "-------------"
|
10
|
+
opt.on('-a', '--agent', 'Agent mode') { Diskmon::Runner.agent }
|
11
|
+
opt.on('-s', '--server', 'Server mode') { Diskmon::Runner.server }
|
12
|
+
opt.separator ""
|
13
|
+
opt.on_tail('-h', '--help') { puts opt; exit }
|
14
|
+
|
15
|
+
opt.parse!
|
16
|
+
end
|
data/diskmon.gemspec
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/diskmon/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Morion Black"]
|
6
|
+
gem.email = ["morion.estariol@gmail.com"]
|
7
|
+
gem.description = %q{Gem for collect statistic from disks}
|
8
|
+
gem.summary = %q{Gem for collect statistic from disks}
|
9
|
+
gem.homepage = ""
|
10
|
+
|
11
|
+
gem.files = ["Gemfile", "LICENSE", "README.md", "Rakefile", "bin/diskmon", "diskmon.gemspec", "lib/diskmon.rb", "lib/diskmon/client/collector.rb", "lib/diskmon/client/config.rb", "lib/diskmon/client/harddisk.rb", "lib/diskmon/client/raidcontroller.rb", "lib/diskmon/client/solarismapdev.rb", "lib/diskmon/client/zpoolstree.rb", "lib/diskmon/server/harddisk.rb", "lib/diskmon/server/harddiskreport.rb", "lib/diskmon/version.rb"]
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "diskmon"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = Diskmon::VERSION
|
17
|
+
gem.add_runtime_dependency 'data_mapper'
|
18
|
+
gem.add_runtime_dependency 'sinatra'
|
19
|
+
gem.add_runtime_dependency 'thin'
|
20
|
+
end
|
data/lib/diskmon.rb
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
require "diskmon/version"
|
2
|
+
require "diskmon/client/config"
|
3
|
+
require "diskmon/client/raidcontroller"
|
4
|
+
require "diskmon/client/collector"
|
5
|
+
|
6
|
+
require 'sinatra'
|
7
|
+
require "thin"
|
8
|
+
require 'data_mapper'
|
9
|
+
require 'dm-validations'
|
10
|
+
require 'pp'
|
11
|
+
require "diskmon/server/harddiskreport"
|
12
|
+
|
13
|
+
module Diskmon
|
14
|
+
|
15
|
+
module Runner
|
16
|
+
|
17
|
+
CONFIG = "/usr/local/etc/diskmon.conf"
|
18
|
+
|
19
|
+
################################################################################
|
20
|
+
# Agent mode
|
21
|
+
################################################################################
|
22
|
+
|
23
|
+
def self.agent
|
24
|
+
|
25
|
+
cfg = Diskmon::Config.new(CONFIG)
|
26
|
+
p cfg if $DEBUG
|
27
|
+
|
28
|
+
raid_ctl = Diskmon::RaidController.new
|
29
|
+
raid_ctl.get_all_stats
|
30
|
+
m = raid_ctl.serialize
|
31
|
+
|
32
|
+
coll = Diskmon::Collector.new
|
33
|
+
send_result = coll.send_report(cfg.collector_url, cfg.collector_login, cfg.collector_password, m)
|
34
|
+
p send_result
|
35
|
+
end
|
36
|
+
|
37
|
+
################################################################################
|
38
|
+
# Server mode
|
39
|
+
################################################################################
|
40
|
+
|
41
|
+
def self.server
|
42
|
+
set :port, 7777
|
43
|
+
|
44
|
+
DataMapper::Logger.new($stdout, :debug)
|
45
|
+
DataMapper::setup(:default, "sqlite3://#{Dir.pwd}/reports.db")
|
46
|
+
|
47
|
+
$DEBUG = true
|
48
|
+
|
49
|
+
DataMapper.finalize
|
50
|
+
|
51
|
+
Diskmon::HardDiskReport.auto_upgrade!
|
52
|
+
|
53
|
+
post '/report/new' do
|
54
|
+
data = request.body.read
|
55
|
+
|
56
|
+
Kernel.puts "// Report from #{request.env['REMOTE_ADDR']}" if $DEBUG
|
57
|
+
|
58
|
+
@m = Marshal.load(data)
|
59
|
+
|
60
|
+
@m.each do |disk|
|
61
|
+
report = Diskmon::HardDiskReport.create(disk.to_hash)
|
62
|
+
report.checksum = disk.checksum
|
63
|
+
|
64
|
+
report.print if $DEBUG
|
65
|
+
|
66
|
+
report.save
|
67
|
+
end
|
68
|
+
|
69
|
+
"OK"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "net/http"
|
2
|
+
|
3
|
+
module Diskmon
|
4
|
+
|
5
|
+
class Collector
|
6
|
+
|
7
|
+
def send_report(url, login, password, dump)
|
8
|
+
c_uri = URI(url)
|
9
|
+
|
10
|
+
p c_uri if $DEBUG
|
11
|
+
|
12
|
+
c_req = Net::HTTP::Post.new(c_uri.path)
|
13
|
+
c_req.basic_auth(login, password)
|
14
|
+
c_req.body = dump
|
15
|
+
c_req.content_type = 'text/plain'
|
16
|
+
|
17
|
+
res = Net::HTTP.start(c_uri.host, c_uri.port) do |http|
|
18
|
+
http.request(c_req)
|
19
|
+
end
|
20
|
+
|
21
|
+
res.code
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require "yaml"
|
2
|
+
|
3
|
+
module Diskmon
|
4
|
+
|
5
|
+
class Config
|
6
|
+
|
7
|
+
def initialize(filepath)
|
8
|
+
h = YAML.load_file(filepath)
|
9
|
+
@collector_url = h["collector_url"]
|
10
|
+
@collector_login = h["collector_login"]
|
11
|
+
@collector_password = h["collector_password"]
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :collector_url
|
15
|
+
attr_reader :collector_login
|
16
|
+
attr_reader :collector_password
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Diskmon
|
2
|
+
|
3
|
+
class HardDisk
|
4
|
+
|
5
|
+
# TODO: convert to bytes
|
6
|
+
# def size(size)
|
7
|
+
# @size =
|
8
|
+
# end
|
9
|
+
|
10
|
+
attr_accessor :dri
|
11
|
+
attr_accessor :ctl_id
|
12
|
+
attr_accessor :port
|
13
|
+
attr_accessor :status
|
14
|
+
attr_accessor :unit
|
15
|
+
attr_accessor :type # sata or sas
|
16
|
+
attr_accessor :vendor_model
|
17
|
+
attr_accessor :smart_status_raw # array of hex numbers
|
18
|
+
attr_accessor :smart_status
|
19
|
+
attr_accessor :size
|
20
|
+
|
21
|
+
attr_accessor :reallocs
|
22
|
+
attr_accessor :age_hours
|
23
|
+
attr_accessor :temperature
|
24
|
+
attr_accessor :serial
|
25
|
+
attr_accessor :spindle_speed # rpms
|
26
|
+
|
27
|
+
attr_accessor :error_events
|
28
|
+
|
29
|
+
attr_accessor :member_of_zpool
|
30
|
+
attr_accessor :read_errors
|
31
|
+
attr_accessor :write_errors
|
32
|
+
attr_accessor :checksum_errors
|
33
|
+
|
34
|
+
attr_accessor :zpool_health
|
35
|
+
attr_accessor :zpool_last_command
|
36
|
+
attr_accessor :zpool_total_space
|
37
|
+
attr_accessor :zpool_free_space
|
38
|
+
|
39
|
+
attr_accessor :system_device
|
40
|
+
attr_accessor :short_device
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,229 @@
|
|
1
|
+
require "diskmon/client/solarismapdev"
|
2
|
+
require "diskmon/client/harddisk"
|
3
|
+
require "diskmon/client/zpoolstree"
|
4
|
+
|
5
|
+
module Diskmon
|
6
|
+
|
7
|
+
class RaidController
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@vendor = get_vendor
|
11
|
+
@disks = []
|
12
|
+
end
|
13
|
+
|
14
|
+
def get_vendor
|
15
|
+
v = `scanpci | grep -i raid`
|
16
|
+
|
17
|
+
case v.downcase
|
18
|
+
when /3ware/ then return :threeware
|
19
|
+
when /adaptec/ then return :adaptec
|
20
|
+
else
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def get_ctl_ids
|
26
|
+
|
27
|
+
ctl_ids = []
|
28
|
+
|
29
|
+
case @vendor
|
30
|
+
|
31
|
+
when :threeware
|
32
|
+
IO.popen("tw_cli show | grep ^c") do |tw_cli_show_io|
|
33
|
+
tw_cli_show_io.each_line { |l| ctl_ids.push( l.split[0] ) }
|
34
|
+
end
|
35
|
+
when :adaptec
|
36
|
+
IO.popen("arcconf getversion | grep 'Controller #'") do |arcconf_getversion_io|
|
37
|
+
arcconf_getversion_io.each_line { |l| ctl_ids.push( l.split[1][1..-1] ) }
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
ctl_ids
|
43
|
+
end
|
44
|
+
|
45
|
+
def get_ctl_disks_info(ctl_id)
|
46
|
+
case @vendor
|
47
|
+
when :threeware
|
48
|
+
|
49
|
+
mapdev = Diskmon::SolarisMapDev.new
|
50
|
+
|
51
|
+
# disks overview
|
52
|
+
#
|
53
|
+
IO.popen("tw_cli /#{ctl_id} show drivestatus | grep ^p") do |tw_cli_drives_io|
|
54
|
+
tw_cli_drives_io.each_line do |l|
|
55
|
+
l_arr = l.split
|
56
|
+
|
57
|
+
d = Diskmon::HardDisk.new
|
58
|
+
|
59
|
+
d.ctl_id = ctl_id[1..-1] # strip 'c'
|
60
|
+
d.port = l_arr[0][1..-1] # strip 'p'
|
61
|
+
d.status = l_arr[1]
|
62
|
+
d.unit = l_arr[2][1..-1] # strip 'u'
|
63
|
+
d.size = l_arr[3..4]
|
64
|
+
d.type = l_arr[5]
|
65
|
+
d.vendor_model = l_arr[8]
|
66
|
+
|
67
|
+
d.dri = "disk://" + Diskmon::Server.get_id + "/" + d.ctl_id + "/" + d.port
|
68
|
+
|
69
|
+
# IO.popen("tw_cli /#{ctl_id}/#{d.port} show smart") do |tw_cli_show_smart_io|
|
70
|
+
|
71
|
+
# d.smart_status_raw = []
|
72
|
+
|
73
|
+
# tw_cli_show_smart_io.each_line do |l|
|
74
|
+
# s = l.split
|
75
|
+
# d.smart_status_raw.push(s) if l[0,1] =~ /[0-9]/ and not s.empty?
|
76
|
+
# end
|
77
|
+
|
78
|
+
# d.smart_status = parse_smart_status(d.smart_status_raw.flatten!)
|
79
|
+
|
80
|
+
# end # show smart
|
81
|
+
|
82
|
+
# full stats
|
83
|
+
#
|
84
|
+
IO.popen("tw_cli /c#{d.ctl_id}/p#{d.port} show all | grep '^/c#{d.ctl_id}/p#{d.port}'") do |tw_cli_show_all_io|
|
85
|
+
|
86
|
+
tw_cli_show_all_io.each_line do |l|
|
87
|
+
|
88
|
+
params_arr = l.split
|
89
|
+
|
90
|
+
case params_arr[1]
|
91
|
+
when "Reallocated" then d.reallocs = params_arr[4]
|
92
|
+
when "Power" then d.age_hours = params_arr[5]
|
93
|
+
when "Temperature" then d.temperature = params_arr[3]
|
94
|
+
when "Serial" then d.serial = params_arr[3]
|
95
|
+
when "Spindle" then d.spindle_speed = params_arr[4]
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end # show all
|
100
|
+
|
101
|
+
d.system_device = "c#{d.ctl_id}t#{d.unit}d0"
|
102
|
+
d.short_device = mapdev.to_short(d.system_device)
|
103
|
+
|
104
|
+
yield d
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
when :adaptec
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def get_all_stats
|
113
|
+
|
114
|
+
ctl_ids = get_ctl_ids
|
115
|
+
|
116
|
+
zp = Diskmon::ZpoolsTree.new
|
117
|
+
|
118
|
+
if not ctl_ids.empty?
|
119
|
+
ctl_ids.each do |ctl_id|
|
120
|
+
get_ctl_disks_info(ctl_id) do |d|
|
121
|
+
|
122
|
+
d.member_of_zpool = zp.which_pool(d.system_device)
|
123
|
+
d.read_errors = zp.read_errors(d.system_device)
|
124
|
+
d.write_errors = zp.write_errors(d.system_device)
|
125
|
+
d.checksum_errors = zp.checksum_errors(d.system_device)
|
126
|
+
|
127
|
+
d.zpool_health = zp.get_pool_param(d.member_of_zpool, "health")
|
128
|
+
d.zpool_last_command = zp.get_pool_param(d.member_of_zpool, "last_command")
|
129
|
+
d.zpool_total_space = zp.get_pool_param(d.member_of_zpool, "total_space")
|
130
|
+
d.zpool_free_space = zp.get_pool_param(d.member_of_zpool, "free_space")
|
131
|
+
|
132
|
+
@disks.push(d)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# TODO: /c3 show events
|
138
|
+
|
139
|
+
p @disks if $DEBUG
|
140
|
+
|
141
|
+
end
|
142
|
+
|
143
|
+
def parse_smart_status(smart_arr)
|
144
|
+
|
145
|
+
# TODO: dig smartmontools sources
|
146
|
+
|
147
|
+
smart_attributes = {
|
148
|
+
'00' => 'Zero_Field',
|
149
|
+
'01' => 'Raw_Read_Error_Rate',
|
150
|
+
'03' => 'Spin_Up_Time',
|
151
|
+
'04' => 'Start_Stop_Count',
|
152
|
+
'05' => 'Reallocated_Sector_Ct',
|
153
|
+
'07' => 'Seek_Error_Rate',
|
154
|
+
'09' => 'Power_On_Hours',
|
155
|
+
'0A' => 'Spin_Retry_Count',
|
156
|
+
'0B' => 'Calibration_Retry_Count',
|
157
|
+
'0C' => 'Power_Cycle_Count',
|
158
|
+
'B8' => 'End-to-End_error',
|
159
|
+
'BB' => 'Reported_Uncorrect',
|
160
|
+
'BC' => 'Command_Timeout',
|
161
|
+
'BD' => 'High Fly Writes',
|
162
|
+
'BE' => 'Airflow Temperature',
|
163
|
+
'C0' => 'Power-off_Retract_Count',
|
164
|
+
'C1' => 'Load_Cycle_Count',
|
165
|
+
'C2' => 'Temperature',
|
166
|
+
'C3' => 'Hardware ECC Recovered',
|
167
|
+
'C4' => 'Reallocation Event Count',
|
168
|
+
'C5' => 'Current Pending Sector Count',
|
169
|
+
'C6' => 'Uncorrectable Sector Count',
|
170
|
+
'C7' => 'UltraDMA CRC Error Count',
|
171
|
+
'C8' => 'Multi-Zone Error Rate',
|
172
|
+
}
|
173
|
+
|
174
|
+
smart_parsed = {}
|
175
|
+
|
176
|
+
i = 0 ; while i < 360 do
|
177
|
+
|
178
|
+
if smart_arr[i+2].to_i != 0
|
179
|
+
|
180
|
+
j = 0 ; while j < 12 do
|
181
|
+
if j == 0
|
182
|
+
smart_param = smart_attributes[smart_arr[i+j+2]]
|
183
|
+
smart_val = ''
|
184
|
+
end
|
185
|
+
|
186
|
+
if j > 4 and j < 11
|
187
|
+
smart_val << smart_arr[i+j+2]
|
188
|
+
end
|
189
|
+
|
190
|
+
j += 1
|
191
|
+
end # while j < 12
|
192
|
+
|
193
|
+
smart_parsed["#{smart_param}"] = smart_val.to_i(10)
|
194
|
+
|
195
|
+
end # if smart_arr
|
196
|
+
|
197
|
+
i += 12
|
198
|
+
|
199
|
+
end # while i
|
200
|
+
|
201
|
+
# p smart_parsed if $DEBUG
|
202
|
+
|
203
|
+
smart_parsed
|
204
|
+
|
205
|
+
end
|
206
|
+
|
207
|
+
def serialize
|
208
|
+
Marshal.dump(@disks)
|
209
|
+
end
|
210
|
+
|
211
|
+
private :get_vendor
|
212
|
+
private :get_ctl_ids
|
213
|
+
private :get_ctl_disks_info
|
214
|
+
|
215
|
+
end
|
216
|
+
|
217
|
+
########## Server
|
218
|
+
|
219
|
+
module Server
|
220
|
+
|
221
|
+
# def get_id
|
222
|
+
# Socket.gethostname.downcase
|
223
|
+
# end
|
224
|
+
|
225
|
+
def self.get_id
|
226
|
+
`/usr/sbin/zlogin df-storage hostname`.chomp
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Diskmon
|
2
|
+
|
3
|
+
class SolarisMapDev
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
|
7
|
+
@full_dev_to_inst = {}
|
8
|
+
|
9
|
+
# "/pci@7a,0/pci8086,340c@5/pci9005,2b5@0/disk@52,0" 11 "sd"
|
10
|
+
IO.foreach("/etc/path_to_inst") do |l|
|
11
|
+
case l.strip
|
12
|
+
when /"sd"$/
|
13
|
+
full_dev = l.split[0].tr('"','')
|
14
|
+
idx = l.split[-2]
|
15
|
+
@full_dev_to_inst[full_dev] = "sd#{idx}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_short(device)
|
21
|
+
begin
|
22
|
+
dev = File.readlink("/dev/rdsk/#{device}").gsub("../../devices", "").split(':')[0]
|
23
|
+
rescue Errno::ENOENT
|
24
|
+
dev = File.readlink("/dev/rdsk/#{device}s0").gsub("../../devices", "").split(':')[0]
|
25
|
+
end
|
26
|
+
|
27
|
+
@full_dev_to_inst[dev]
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Diskmon
|
2
|
+
|
3
|
+
class ZpoolsTree
|
4
|
+
def initialize
|
5
|
+
|
6
|
+
@disks = {}
|
7
|
+
@pools = {}
|
8
|
+
|
9
|
+
IO.popen("/usr/sbin/zpool status") do |zpool_status_io|
|
10
|
+
|
11
|
+
pool_name = ''
|
12
|
+
|
13
|
+
zpool_status_io.each_line do |l|
|
14
|
+
case l
|
15
|
+
|
16
|
+
when /pool: /
|
17
|
+
pool_name = l.split[1]
|
18
|
+
@pools[pool_name] = {}
|
19
|
+
@pools[pool_name]["health"] = `/usr/sbin/zpool get health #{pool_name} | grep #{pool_name}`.split[2]
|
20
|
+
@pools[pool_name]["free_space"] = `/usr/sbin/zpool get free #{pool_name} | grep #{pool_name}`.split[2]
|
21
|
+
@pools[pool_name]["total_space"] = `/usr/sbin/zpool get allocated #{pool_name} | grep #{pool_name}`.split[2]
|
22
|
+
|
23
|
+
history_events = []
|
24
|
+
|
25
|
+
IO.popen("/usr/sbin/zpool history #{pool_name}") do |zpool_history_io|
|
26
|
+
|
27
|
+
zpool_history_io.each_line do |l|
|
28
|
+
case l
|
29
|
+
when /^[0-9]/ then history_events.push(l)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
@pools[pool_name]["last_command"] = history_events[-1].chomp
|
36
|
+
|
37
|
+
when /c[0-9]t/
|
38
|
+
disk_name = l.split[0]
|
39
|
+
|
40
|
+
@disks[disk_name] = {}
|
41
|
+
@disks[disk_name]["zpool"] = pool_name
|
42
|
+
|
43
|
+
@disks[disk_name]["read_errors"] = l.split[2]
|
44
|
+
@disks[disk_name]["write_errors"] = l.split[3]
|
45
|
+
@disks[disk_name]["checksum_errors"] = l.split[4]
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# p @disks if $DEBUG
|
53
|
+
# p @pools if $DEBUG
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
def which_pool(device)
|
58
|
+
@disks[device]["zpool"]
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_errors(device)
|
62
|
+
@disks[device]["read_errors"]
|
63
|
+
end
|
64
|
+
|
65
|
+
def write_errors(device)
|
66
|
+
@disks[device]["write_errors"]
|
67
|
+
end
|
68
|
+
|
69
|
+
def checksum_errors(device)
|
70
|
+
@disks[device]["checksum_errors"]
|
71
|
+
end
|
72
|
+
|
73
|
+
# free_space
|
74
|
+
# total_space
|
75
|
+
# last_command
|
76
|
+
# health
|
77
|
+
def get_pool_param(pool, param)
|
78
|
+
@pools[pool][param]
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'digest'
|
2
|
+
|
3
|
+
module Diskmon
|
4
|
+
|
5
|
+
class HardDisk
|
6
|
+
|
7
|
+
# credit: http://stackoverflow.com/a/5031637
|
8
|
+
def to_hash
|
9
|
+
Hash[instance_variables.map { |var| [var[1..-1].to_sym, instance_variable_get(var)] }]
|
10
|
+
end
|
11
|
+
|
12
|
+
def checksum
|
13
|
+
as_str = ''
|
14
|
+
instance_variables.sort.map { |k| as_str << "#{k}=#{instance_variable_get(k)};" }
|
15
|
+
p as_str if $DEBUG
|
16
|
+
Digest::MD5.hexdigest(as_str)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "data_mapper"
|
2
|
+
|
3
|
+
module Diskmon
|
4
|
+
|
5
|
+
class HardDiskReport
|
6
|
+
include DataMapper::Resource
|
7
|
+
|
8
|
+
property :dri , String
|
9
|
+
property :ctl_id , String
|
10
|
+
property :port , String
|
11
|
+
property :status , String
|
12
|
+
property :unit , String
|
13
|
+
property :type , String
|
14
|
+
property :vendor_model , String
|
15
|
+
property :smart_status_raw , String
|
16
|
+
property :smart_status , String
|
17
|
+
property :size , String
|
18
|
+
property :reallocs , String
|
19
|
+
property :age_hours , String
|
20
|
+
property :temperature , String
|
21
|
+
property :serial , String
|
22
|
+
property :spindle_speed , String
|
23
|
+
property :error_events , String
|
24
|
+
property :member_of_zpool , String
|
25
|
+
property :disk_space_used , String
|
26
|
+
|
27
|
+
property :error_events , String
|
28
|
+
|
29
|
+
property :member_of_zpool , String
|
30
|
+
property :read_errors , String
|
31
|
+
property :write_errors , String
|
32
|
+
property :checksum_errors , String
|
33
|
+
|
34
|
+
property :zpool_health , String
|
35
|
+
property :zpool_last_command , String
|
36
|
+
property :zpool_total_space , String
|
37
|
+
property :zpool_free_space , String
|
38
|
+
|
39
|
+
property :system_device , String
|
40
|
+
property :short_device , String
|
41
|
+
|
42
|
+
property :created_at , EpochTime
|
43
|
+
property :checksum , String, :key => true, :unique => true
|
44
|
+
|
45
|
+
def print
|
46
|
+
instance_variables.sort.map { |k| printf( "%-20s => %s\n", k, instance_variable_get(k) ) }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
end
|
metadata
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: diskmon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Morion Black
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-12 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: data_mapper
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ! '>='
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sinatra
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: thin
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Gem for collect statistic from disks
|
63
|
+
email:
|
64
|
+
- morion.estariol@gmail.com
|
65
|
+
executables:
|
66
|
+
- diskmon
|
67
|
+
extensions: []
|
68
|
+
extra_rdoc_files: []
|
69
|
+
files:
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- bin/diskmon
|
75
|
+
- diskmon.gemspec
|
76
|
+
- lib/diskmon.rb
|
77
|
+
- lib/diskmon/client/collector.rb
|
78
|
+
- lib/diskmon/client/config.rb
|
79
|
+
- lib/diskmon/client/harddisk.rb
|
80
|
+
- lib/diskmon/client/raidcontroller.rb
|
81
|
+
- lib/diskmon/client/solarismapdev.rb
|
82
|
+
- lib/diskmon/client/zpoolstree.rb
|
83
|
+
- lib/diskmon/server/harddisk.rb
|
84
|
+
- lib/diskmon/server/harddiskreport.rb
|
85
|
+
- lib/diskmon/version.rb
|
86
|
+
homepage: ''
|
87
|
+
licenses: []
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
none: false
|
94
|
+
requirements:
|
95
|
+
- - ! '>='
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
98
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
|
+
none: false
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
requirements: []
|
105
|
+
rubyforge_project:
|
106
|
+
rubygems_version: 1.8.23
|
107
|
+
signing_key:
|
108
|
+
specification_version: 3
|
109
|
+
summary: Gem for collect statistic from disks
|
110
|
+
test_files: []
|