File Coverage

blib/lib/Zabbix/Check/Disk.pm
Criterion Covered Total %
statement 22 139 15.8
branch 0 68 0.0
condition 0 21 0.0
subroutine 7 14 50.0
pod 0 3 0.0
total 29 245 11.8


line stmt bran cond sub pod time code
1             package Zabbix::Check::Disk;
2             =head1 NAME
3              
4             Zabbix::Check::Disk - Zabbix check for disk
5              
6             =head1 VERSION
7              
8             version 1.11
9              
10             =head1 SYNOPSIS
11              
12             Zabbix check for disk
13              
14             =cut
15 1     1   1365 use strict;
  1         4  
  1         43  
16 1     1   8 use warnings;
  1         3  
  1         40  
17 1     1   15 use v5.10.1;
  1         5  
18 1     1   6 use JSON;
  1         1  
  1         8  
19 1     1   163 use Lazy::Utils;
  1         8  
  1         155  
20              
21 1     1   6 use Zabbix::Check;
  1         2  
  1         105  
22              
23              
24             BEGIN
25             {
26 1     1   6 require Exporter;
27 1         2 our $VERSION = '1.11';
28 1         8 our @ISA = qw(Exporter);
29 1         2 our @EXPORT = qw(_discovery _bps _iops _ioutil);
30 1         1715 our @EXPORT_OK = qw();
31             }
32              
33              
34             sub disks
35             {
36 0     0 0   my $result = {};
37 0           for my $blockpath (glob("/sys/dev/block/*"))
38             {
39 0           my $uevent = file_get_contents("$blockpath/uevent");
40 0 0         next unless defined($uevent);
41 0           my ($major) = $uevent =~ /^\QMAJOR=\E(.*)/m;
42 0           my ($minor) = $uevent =~ /^\QMINOR=\E(.*)/m;
43 0           my ($devname) = $uevent =~ /^\QDEVNAME=\E(.*)/m;
44 0           my ($devtype) = $uevent =~ /^\QDEVTYPE=\E(.*)/m;
45 0           my $devpath = "/dev/$devname";
46 0 0         my $disk = {
    0          
    0          
47             blockpath => $blockpath,
48             devname => $devname,
49             devtype => $devtype,
50             devpath => $devpath,
51             major => $major,
52             minor => $minor,
53             size => defined($_ = file_get_contents("$blockpath/size"))? trim($_)*512: undef,
54             removable => defined($_ = file_get_contents("$blockpath/removable"))? trim($_): undef,
55             partition => defined($_ = file_get_contents("$blockpath/partition"))? trim($_): undef,
56             dmname => undef,
57             dmpath => undef,
58             };
59 0 0         if (defined(my $dmname = file_get_contents("$blockpath/dm/name")))
60             {
61 0           $dmname = trim($dmname);
62 0           $disk->{dmname} = $dmname;
63 0           $disk->{dmpath} = "/dev/mapper/$dmname";
64             }
65 0 0         my $dmpath = defined($disk->{dmpath})? $disk->{dmpath}: "";
66 0 0         for my $mount (grep(/^(\Q$disk->{devpath}\E|\Q$dmpath\E)\s+/, defined($_ = file_get_contents("/proc/mounts"))? split("\n", $_): ()))
67             {
68 0           $mount = trim($mount);
69 0           my ($mountname, $mountpoint, $fstype) = $mount =~ /^(\S+)\s+(\S+)\s+(\S+)\s+/;
70 0 0         next unless $mountname =~ /^\Q$disk->{devpath}\E|\Q$dmpath\E$/;
71 0           $disk->{mountpoint} = $mountpoint;
72 0           $disk->{fstype} = $fstype;
73             }
74 0           $result->{$devname} = $disk;
75             }
76 0           return $result;
77             }
78              
79             sub stats
80             {
81 0     0 0   my $result = {};
82 0           my $disks = disks();
83 0           for my $devname (keys %$disks)
84             {
85 0           my $disk = $disks->{$devname};
86 0 0         my $stat_line = defined($_ = file_get_contents("$disk->{blockpath}/stat"))? trim($_): "";
87 0 0         next unless $stat_line;
88 0           my $stat = { 'epoch' => time() };
89             (
90             $stat->{readIOs},
91             $stat->{readsMerges},
92             $stat->{readSectors},
93             $stat->{readWaits},
94             $stat->{writeIOs},
95             $stat->{writesMerges},
96             $stat->{writeSectors},
97             $stat->{writeWaits},
98             $stat->{inFlight},
99             $stat->{IOTicks},
100             $stat->{totalWaits},
101 0           ) = $stat_line =~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/;
102 0           $result->{$devname} = $stat;
103             }
104 0           return $result;
105             }
106              
107             sub analyze_stats
108             {
109 0     0 0   my $now = time();
110 0           my $stats;
111             my $old_stats;
112 0           my $tmp_prefix = (caller(0))[3];
113 0           $tmp_prefix =~ s/\Q::\E/-/g;
114 0           $tmp_prefix = "/tmp/".$tmp_prefix.".";
115 0           for my $tmp_path (sort {$b cmp $a} glob("$tmp_prefix*"))
  0            
116             {
117 0 0         if (my ($epoch, $pid) = $tmp_path =~ /^\Q$tmp_prefix\E(\d*)\.(\d*)/)
118             {
119 0 0         if ($now-$epoch < 1*60)
120             {
121 0 0         if (not $stats)
122             {
123 0           my $tmp = file_get_contents($tmp_path);
124 0 0         eval { $stats = from_json($tmp) } if $tmp;
  0            
125             }
126 0           next;
127             }
128 0 0         if (not $old_stats)
129             {
130 0           my $tmp = file_get_contents($tmp_path);
131 0 0         eval { $old_stats = from_json($tmp) } if $tmp;
  0            
132 0 0 0       next unless not $tmp or $@;
133             }
134 0 0         next unless $now-$epoch > 2*60;
135             }
136 0           unlink($tmp_path);
137             }
138 0 0         unless ($stats)
139             {
140 0           $stats = stats();
141 0           my $tmp;
142 0           eval { $tmp = to_json($stats, {pretty => 1}) };
  0            
143 0 0         file_put_contents("$tmp_prefix$now.$$", $tmp) if $tmp;
144             }
145 0 0         return unless $old_stats;
146 0           my $result = {};
147 0           for my $devname (keys %$stats)
148             {
149 0           my $stat = $stats->{$devname};
150 0           my $old_stat = $old_stats->{$devname};
151 0 0         next unless defined $old_stat;
152 0           my $diff = $stat->{epoch} - $old_stat->{epoch};
153 0 0         next unless $diff;
154 0           $result->{$devname} = {};
155              
156 0           my $sector;
157             my $io;
158              
159 0           $sector = $stat->{readSectors} - $old_stat->{readSectors};
160 0           $io = $stat->{readIOs} - $old_stat->{readIOs};
161 0           $result->{$devname}->{bps_read} = 512*$sector/$diff;
162 0           $result->{$devname}->{iops_read} = $io/$diff;
163              
164 0           $sector = $stat->{writeSectors} - $old_stat->{writeSectors};
165 0           $io = $stat->{writeIOs} - $old_stat->{writeIOs};
166 0           $result->{$devname}->{bps_write} = 512*$sector/$diff;
167 0           $result->{$devname}->{iops_write} = $io/$diff;
168              
169 0           $sector = $stat->{readSectors} - $old_stat->{readSectors} + $stat->{writeSectors} - $old_stat->{writeSectors};
170 0           $io = $stat->{readIOs} - $old_stat->{readIOs} + $stat->{writeIOs} - $old_stat->{writeIOs};
171 0           $result->{$devname}->{bps_total} = 512*$sector/$diff;
172 0           $result->{$devname}->{iops_total} = $io/$diff;
173              
174 0           $result->{$devname}->{ioutil} = 100*($stat->{IOTicks} - $old_stat->{IOTicks})/(1000*$diff);
175             }
176 0           return $result;
177             }
178              
179             sub _discovery
180             {
181 0     0     my ($removable) = @_;
182 0           my @items;
183 0           my $disks = disks();
184 0           for my $devname (keys %$disks)
185             {
186 0           my $disk = $disks->{$devname};
187 0 0 0       next if $devname =~/^loop\d*$/i or $devname =~ /^ram\d*$/i;
188 0 0 0       next if not $removable and $disk->{removable};
189 0           push @items, $disk;
190             }
191 0           return print_discovery(@items);
192             }
193              
194             sub _bps
195             {
196 0     0     my ($devname, $type) = map(zbx_decode($_), @ARGV);
197 0 0 0       return "" unless defined($devname) and $type and $type =~ /^read|write|total$/;
      0        
198 0           my $result = 0;
199 0           my $analyzed = analyze_stats();
200 0 0         my $status = $analyzed->{$devname} if $analyzed;
201 0 0         $result = sprintf("%.2f", $status->{"bps_$type"}) if $status;
202 0           print $result;
203 0           return $result;
204             }
205              
206             sub _iops
207             {
208 0     0     my ($devname, $type) = map(zbx_decode($_), @ARGV);
209 0 0 0       return "" unless defined($devname) and $type and $type =~ /^read|write|total$/;
      0        
210 0           my $result = 0;
211 0           my $analyzed = analyze_stats();
212 0 0         my $status = $analyzed->{$devname} if $analyzed;
213 0 0         $result = sprintf("%.2f", $status->{"iops_$type"}) if $status;
214 0           print $result;
215 0           return $result;
216             }
217              
218             sub _ioutil
219             {
220 0     0     my ($devname) = map(zbx_decode($_), @ARGV);
221 0 0         return "" unless defined($devname);
222 0           my $result = 0;
223 0           my $analyzed = analyze_stats();
224 0 0         my $status = $analyzed->{$devname} if $analyzed;
225 0 0         $result = sprintf("%.2f", $status->{"ioutil"}) if $status;
226 0           print $result;
227 0           return $result;
228             }
229              
230              
231             1;
232             __END__